【Windows】バッチスクリプトでヘッダーレコード(先頭行)を除外

いつもお世話になっております。
RfromL.comです。

今回はヘッダーレコード(先頭行)を除外して出力するバッチスクリプトについてです。

CONTENTS

1.はじめに
 1-1.目的
 1-2.入力ファイル仕様
2.処理概要
 2-1.ディレクトリ構成
 2-2.全体処理概要図
 2-3.各処理の内容
3.作成スクリプト
 3-1.コード内容
 3-2.メイン処理部分のコード解説
4.使用方法
5.おわりに


1.はじめに

1-1.目的

やりたいことは下図のようなヘッダーのついたファイル(株価情報のサイトから取得した情報をもとに、手作成したファイル)からヘッダーレコードを除外して、データレコードのみのファイルを作成します。

1-2.入力ファイル仕様

  • ファイルレイアウトはKEY項目である先頭2項目「基準年月日」「証券コード(銘柄コード)」以降は今回の処理内容にあまり関係がないので割愛
  • ファイル単位は「基準年月日」ごとに1ファイル
  • ファイル内容はインフラファンドの銘柄ごとの価格情報
    (終値、NAV倍率、分配金利回り等々の情報)
  • 改行コードは「CRLF」
  • 文字コードは「SJIS」
  • ファイル形式は「タブ区切りCSV」
  • 囲み文字(ダブルクォーテーション、シングルクオーテーションなど)は「なし」

入力のファイルは基準年月日ごとに1ファイルづつ存在するため、バッチスクリプト内にファイルIDを固定で指定せず特定のディレクトリ(入力用ディレクトリ)内に格納されたファイル全てを処理対象とするようにします。

ファイルIDが変わるごとにバッチスクリプトを修正するのも、処理の為に入力ファイルをリネームするのもめんどくさいので・・・


2.処理概要

2-1.ディレクトリ構成

今回作成する処理のディレクトリ構成は以下の通りです。

上記の「HeaderExclusion」のディレクトリ構成も含めて今回作成する処理の一部なのでバッチスクリプトは上記の構成、配置になっていることを前提で作成します。

2-2.全体処理概要図

処理全体の流れは下図のようになります。

各処理については次項で記載します。

2-3.各処理の内容

①中間ファイルクリア
前回実行時に作成された中間ファイル「prices_trn.txt」が存在した場合は該当ファイルを削除する。

②前回出力ファイルバックアップ
前回実行時に作成された出力ファイル「prices_out.txt」が存在した場合はファイル名に日時情報を付与した「prices_out_YYYYMMDD_hhmmss.txt」形式のファイル名にリネームを行いバックアップする。

③ヘッダー除外・ファイル結合処理
「IN」ディレクトリに格納されているファイルを全て読み込み、ヘッダーレコードを除外して「prices_trn.txt」の1ファイルに出力する。

④ソート処理(昇順)
ヘッダーレコードを除外したファイル「prices_trn.txt」を読み込み、「基準年月日」、「証券コード」の昇順でソートして「prices_out.txt」に出力する。
なお、Windowsコマンドの「sort」コマンドにソートKEYを指定する機能は無く、レコード内の先頭からソート(※)するので結果的に先頭2項目「基準年月日」、「証券コード」の昇順でソートされる形です。

※)オプション「/+n」で各行の比較を始める文字番号の指定はできます。(「sort /?」実行結果より)


3.作成スクリプト

3-1.コード内容

用意するスクリプトファイルのコード内容は以下の通りです。

[ HeaderExclusion.bat ]

@echo off
@rem ------------------------------------------------------------
@rem システム名         : RfromL.com
@rem 処理ID             : HeaderExclusion
@rem 処理名             : ヘッダーレコード除外処理
@rem 処理内容           : ①ヘッダーレコード除外
@rem                    : ②入力ファイル結合
@rem                    : ③結合ファイルを昇順でソート
@rem 作成日             : 2022.07.25
@rem 作成者             : S.Takaaze
@rem パラメータ         : なし
@rem 備考               : 
@rem 
@rem *** 修正履歴 ***********************************************
@rem No.  日付        修正者              内容
@rem 001  2022.07.25  S.Takaaze           新規作成
@rem 
@rem ------------------------------------------------------------
@rem - 初期処理
@rem ------------------------------------------------------------
:STEP000

@rem 実行バッチファイルディレクトリ設定
set CMD_PATH=%~dp0

@rem 処理ID設定(バッチファイル名)
set BATCH_ID=%~n0



@rem ------------------------------------------------------------
@rem - 日時情報取得処理
@rem ------------------------------------------------------------
:STEP010

@rem システム日付取得(YYYY=年,MM=月,DD=日,WK=曜日)
set YYYY=%date:~0,4%
set MM=%date:~5,2%
set DD=%date:~8,2%
set WK=%date:~0,1%

@rem システム時刻取得(HH=時)
@rem ※HH部分(時間部分)の1文字目をチェックして
@rem   空白の場合は頭ゼロ埋めでセット
set HHCHK=%time:~0,1%

if "%HHCHK%"==" " (
  set HH=0%time:~1,1%
) else (
  set HH=%time:~0,2%
)

@rem システム時刻取得(MI=分,SS=秒)
set MI=%time:~3,2%
set SS=%time:~6,2%

set DATETIME="%YYYY%%MM%%DD%_%HH%%MI%%SS%"



@rem ------------------------------------------------------------
@rem - ファイルディレクトリ設定
@rem ------------------------------------------------------------
:STEP020

@rem 入力ファイルディレクトリ
set IDIR=%CMD_PATH%\IN

@rem 中間ファイルディレクトリ
set TDIR=%CMD_PATH%\TRN

@rem 出力ファイルディレクトリ
set ODIR=%CMD_PATH%\OUT



@rem ------------------------------------------------------------
@rem - ファイルID設定
@rem ------------------------------------------------------------
:STEP030

@rem 中間ファイルID
set TFILEID001=prices_trn.txt

@rem 出力ファイルID
set OFILEID001=prices_out.txt

@rem 前回出力ファイルバックアップID
set BFILEID001=prices_out_%DATETIME%.txt



@rem ------------------------------------------------------------
@rem - ファイル設定
@rem ------------------------------------------------------------
:STEP040

@rem 中間ファイル
set TFILE001=%TDIR%\%TFILEID001%

@rem 出力ファイル
set OFILE001=%ODIR%\%OFILEID001%



@rem ------------------------------------------------------------
@rem - 前処理 : 中間ファイルクリア処理
@rem ------------------------------------------------------------
:STEP050

if exist "%TFILE001%" del "%TFILE001%"



@rem ------------------------------------------------------------
@rem - 前処理 : 前回出力ファイルバックアップ処理
@rem ------------------------------------------------------------
:STEP060

if exist "%OFILE001%" ren "%OFILE001%" "%BFILEID001%"



@rem ------------------------------------------------------------
@rem - ヘッダー除外・ファイル結合処理
@rem ------------------------------------------------------------
:STEP070

for /F %%i in ('dir /b %IDIR%') do (
  for /F "skip=1 tokens=* usebackq" %%j in ("%IDIR%\%%i") do @echo %%j>> "%TFILE001%"
)



@rem ------------------------------------------------------------
@rem - ソート処理(昇順)
@rem ------------------------------------------------------------
:STEP080

sort "%TFILE001%" /O "%OFILE001%"



@rem ------------------------------------------------------------
@rem - 正常終了処理
@rem ------------------------------------------------------------
:END

exit 0


補足
①コメント部分(「@rem」の行)は不要であれば削除してください。
②「STEP010」の日時情報取得処理部分は、過去記事「【Windows】日付・曜日・時刻をクリップボードにコピーするバッチスクリプト」で使用した方法を流用しています。

3-2.メイン処理部分のコード解説

今回作成したバッチスクリプトでは以下の機能がありますが

①中間ファイルクリア
②前回出力ファイルバックアップ
③ヘッダー除外・ファイル結合処理
④ソート処理(昇順)

このなかでもメインの処理部分である「ヘッダー除外・ファイル結合処理」のコード内容について解説します。

[対象のコード]

for /F %%i in ('dir /b %IDIR%') do (
  for /F "skip=1 tokens=* usebackq" %%j in ("%IDIR%\%%i") do @echo %%j>> "%TFILE001%"
)

このコードで使用しているコマンド・命令文の括りは以下の2つです。

①for文 + dirコマンド
②for文 + echoコマンド + >>コマンド

それぞれ分けて以下に解説します。

①変数「%IDIR%」に格納された入力ファイルディレクトリに対し、「dir」コマンドを/bオプション(ファイルIDのみを表示)指定で実行してファイル一覧を出力します。
ひとつめのfor文でdirコマンドの実行結果を1件ずつ次のfor文に渡します。

以下、dirコマンドヘルプの赤枠部分のオプションを使用。


ひとつめのfor文では、以下のfor文ヘルプの赤枠部分のコマンド拡張機能を使用。



②「①」のfor文で取得した「dir」コマンドの実行結果1件分に対し、ふたつめのfor文で”skip=1″を指定することで、1レコード目の読み込みをスキップします。
2レコード目以降は「echo」コマンドで標準出力し、指定したファイルに追記する「>>」コマンドで出力ファイルにレコード内容を出力します。

ふたつめのfor文で使用では以下、for文ヘルプの赤枠部分を使用。

ヘルプ部分については画像を提示しましたが、自身の環境でも「for /?」コマンドを実行して実際に確認してみてください。


4.使用方法

使用方法の流れだけを示すと以下の通りです。

①「HeaderExclusion」をディレクトリごと配置する。
(ディレクトリ構成を崩さず、ディレクトリごとの移動であればどこに配置しても実行できます。)
②「IN」ディレクトリ配下に入力ファイルを配置する。
③「HeaderExclusion.bat」をダブルクリックして実行する。
④「OUT」ディレクトリ配下に出力ファイルが作成される。


実際に流れにそって使用した結果を以降に記載します。

①バッチスクリプトの配置
今回は「C:\RfromL_com\prod\」というディレクトリ階層を作成し、配下に「HeaderExclusion」をディレクトリごと配置しました。



②入力ファイルの配置
「HeaderExclusion\IN」ディレクトリ配下に処理したいファイルを配置します。
今回は7月25日~7月27日の3日分3ファイルを配置しました。


配置した入力ファイルの内容は以下の通りです。

1ファイル当たりのレコード数は8レコード。
それぞれ先頭1レコードがヘッダーレコードなので、3ファイルのデータレコード合計は21レコードです。

③バッチスクリプトを実行
「HeaderExclusion.bat」をダブルクリックして実行します。



④ファイルの出力結果を確認
「OUT」ディレクトリ配下を確認すると、入力ファイルが1ファイルになって出力されます。



実際にファイルを開いて内容を確認すると、ヘッダーレコード無しで21レコード出力されています。

入力ファイルのデータレコード件数が21レコードだったので、件数ベースで入力と出力で一致していることが分かります。

使用方法については以上です。


5.おわりに

今回は自分で手作成したファイルなので、最初からヘッダー無しでファイルを作ればいいだけの話なんですが、ファイルの元々の目的がヘッダーも必要な使い方だったのでヘッダー付きになっていました。

ファイルが貯まってきたところで、DBに突っ込んで別の使い道ないかなぁと思ったのですが、貯まったファイルから一個一個ヘッダーを削除するのが面倒という理由でバッチスクリプトを作成することになりました。

実際のITの現場でも、ヘッダー付きファイルが入力になる場合というのも稀にあります。
(実際に経験した現場であったような気がするけどなかったかな?)
その場合にとる対策は今回のように自分でヘッダーを除外することも含めて以下の2種類です。

①ファイルからヘッダーを除外して処理する。
②入力のファイルを作成する時点でヘッダーを付与しない。

ファイルの作成元が自システム(自分たちの所属する企業のシステム)内なのか、外部システムなのかでも変わってきます。

まず外部システムから連携されるファイルの場合、ヘッダー無しでファイル作成してもらうには、外部システム側に修正してもらうことになるのでその分のコストが発生します。(たいした修正じゃないけど・・・)

自システム内なら自分で修正するか、他チームの処理で作成されるファイルであれば他チームに修正依頼するかはまだ容易そうです。

また、「元々ヘッダー付きで使用することを想定して出力したファイルを無理やり入力ファイルにすることになった。」という場合だと、既にヘッダー付きで使用されてしまっているので、データだけを使いたいなら今回のようにファイルからヘッダーを除外する処理を作るしかないように思います。

結局は、さまざまな状況があるのでその時々で対応するしかないです。

以上です。
宜しくお願い致します。