おはようございます。こんにちは。こんばんは。
ミッド(@mid_v_lab)です
PowerShellで複数ファイル(CSVファイル)を統合したかったので、そのコードを書いてみました。
コード、フォルダ構成、解説は下記となります。
コード
# 定義
$workdir = “.\”
$inputdir = $workdir + “in\”
$outputdir = $workdir + “out\”
$confdir = $workdir + “conf\”
$filetype = “*.csv”
$outputFile = $outputdir + “all.csv”
# ヘッダー
$header = $confdir + “header.txt”
# 本処理
# inフォルダ配下のファイルを読み取り
$files = Get-ChildItem $inputdir -include $filetype -Recurse
# マージファイルにヘッダを追加
Get-Content $header | Out-File $outputFile
# マージファイルに1行目(ヘッダ)を抜かしてファイル出力
$files | Foreach-Object { $_ | Get-Content | Select-Object -Skip 1 | Out-File $outputFile -Append }
exit
フォルダ構成
フォルダ構成としては下記な感じです。
<フォルダ構成>
当スクリプトと同じディレクトリに「in」「out」「conf」というフォルダを作成し、「in」のフォルダに結合したいファイルを格納します。
<inフォルダ内>
ファイルの中身はこんな感じです。
<1.csvのデータ内容>
ヘッダーに「date,test,count」、データに「8/1,1111,5」と適当な値を入れています。(1.csv~5.csvで値を変えても良いですし、違う値を入れてもOKです)
confフォルダに付与するヘッダーを事前に準備しておきます。
<confフォルダ内>
<header.txtの中身>
スクリプトを実行後、「out」フォルダに「all.csv」というファイルが出来上がり、ヘッダー1行であとはデータが統合されたファイルが出来上がります。
<outフォルダ内>
<all.csvの中身>
詳しくない人に解説
コードとフォルダ構成をそのままコピって頂ければそのまま使えると思いますが、若干解説しておきます。
定義部分
「$workdir = “.\”」は作業フォルダです。
「.\」でスクリプトのあるフォルダを指しています。規程せずにinとoutのフォルダパスを絶対パスで直接指定しても良いんですが、長いスクリプトを書くことを想定したり、作った場所とか違う場所で動かすことを想定すると、別だししておく方が改修がしやすいです。
現状は分かりやすく「.\」指定していますが、変な挙動をする場合もあるので、作業フォルダは絶対パスで指定しておくと安心です。
<過去記事>
$inputdir = $workdir + “in\”
$outputdir = $workdir + “out\”
$confdir = $workdir + “conf\”
上記は作業フォルダ配下のどこにあるかを指定しています。作業フォルダが変わっても、こちらは修正しなくて良い部分です。
「$filetype = “*.csv”」は処理部分において、「in」フォルダの中のどういうファイルを取得してくるか?の条件部分になります。
今回は「CSVファイル(*.csv)」を指定していますが、テキストファイルでも「*.txt」で指定すればOKですし、拡張子関わらず「in」に入っているもの全部であれば「*.*」とかで大丈夫です。
基本は同一フォーマットの複数ファイルを処理することを想定してますので、拡張子は決まっていて、日付で抽出したいとかだと思っています。「(当月日付).csv」で抽出するとか!
「$outputFile = $outputdir + “all.csv”」はマージしたファイル名を規定しています。「all.csv」が嫌だ!という場合は好きなファイル名に変更してください。1か月分のログを統合するなどの場合は、日付を入れても良いと思います。
下記の過去記事を参考に、上手く組み合わせて使ってください。
<過去記事>
ヘッダー部分
「$header = $confdir + “header.txt”」は追加するヘッダーのファイルを指定しています。
定義に含めても良かったのですが、なんとなく別だししておきました。
本処理部分
「$files = Get-ChildItem $inputdir -include $filetype -Recurse」で「inフォルダ」の中に入っている、「*.csv」のファイル形式のファイルを読み取って、「$files」という変数に格納しています。(今回は1.csv~5.csvが対象)
「Get-Content $header | Out-File $outputFile」で、まずヘッダー1行をマージするファイルに書き込みしています。「Get-Content」でファイルの中身を読み取って、「|(パイプ)」で処理をつなげて、「Out-File」で「all.csv」に書き込んでいます。
最後に、読み取ったCSVファイル(1.csv~5.csv)のヘッダー(1行目)を除外して、「all.csv」に書き込んでいきます。
ごちゃごちゃしてるので、分解して解説します。
「$files | Foreach-Object {}」で、「$files」の変数を1つずつ「{}」内の処理を行うことを宣言しています。
「1.csvに対して、{}の処理、2.csvに対して、{}の処理・・・」ということです。
「{}」の処理を見てみると、「{ $_ | Get-Content | Select-Object -Skip 1 | Out-File $outputFile -Append }」のように記載されています。
「$_」は読み取ったファイルを示しているので、今回は「1.csv(~5.csv)」で、これを「Get-Content」でファイルの中身を読み取り、
「Select-Object -Skip 1」で、読み取ったファイルの1行目を飛ばして、
「Out-File $outputFile -Append」で、「all.csv」に追記モードで書き込みを行っています。「-Append」が追記するオプションになり、オプションを付けないと上書きになりますので、
「-Append」を付けない場合「all.csv」の中身は、最後に処理された「5.csv」のヘッダーなしのデータとなります。
注意点
最後に注意点です。
結合するファイルの文字コードによっては、all.csvが文字化けすることがあると思います。
その場合は、「Get-Content」で読み込む時と「Out-File」で出力するときに、「-Encoding」で文字コードを指定してあげると良いです。
軽い気持ちで作り始めたら、意外と変なところに引っかかって確認に時間が掛かったの巻。
こういうスクリプトは1回作っておけば、ウナギのたれのように継ぎ足しできるので便利ですね。
ではでは