MacのPowerShellでCSVファイルを分割してみた。

異なるシステム間でデータをエクスポート・インポートする場合、CSV ファイルを用いるケースが割と多いかと思います。

そして、そのデータが数千行、あるいはそれ以上ある場合、インポート先システムの仕様や負荷・所要時間等を考慮し、複数回に分けたほうがよいこともあります。

しかし、テキストエディタや Excel を使って手作業で分割するのはとても面倒です。

 

 

そこで、PowerShell で CSV ファイルを分割するためのスクリプトを書いてみました。
2015 年 4 月 21 日の記事の逆です。)

せっかくなので先日 Mac にインストールした Powershell 7.1.1 を使ってみました。
(ファイルは全てカレントディレクトリにあるものとします。)

分割する CSV は、12 名分の名簿「members.csv」です。

 


これを 5 名ずつ分割し、
members-1-3.csv」、
members-2-3.csv」、
members-3-3.csv」の 3 ファイルにすることを目指します。
< 1 >
兎にも角にも、まずは「members.csv」を読み込み、変数「$original」とします。

$original = import-csv members.csv

 

< 2 >
データ部が全部で何行あるか把握し、変数「$whole」とします。

$whole = ($original).count

 

【余談】
もし < 1 > で「import-csv」ではなく「get-content」を使っていた場合、< 2 >で得られる値は 12 ではなく 13 になってしまいます。
単なるテキストファイルとして読み込むため、フィールド名の行も含めてカウントしてしまうのです。

 

< 3 >
5 名ずつ分割することで幾つのファイルになるか計算し、変数「$number」とします。

単純に割り算すれば、「12÷5=2.4」となります。
この小数点以下を切り上げて整数「3」にするためには、[math] クラスの Ceiling メソッドを用います。

$number = [math]::ceiling($whole/5)

※ 5,000 名ずつ分割したい場合は、末尾の値を 5,000 にします。

 

ここまでが下準備です。
いよいよ本格的な分割に入ります。

 

< 4 >
Excel のような表計算ソフトであれば、「一番上の行」は「1」行目です。

しかし、Powershell の場合は「一番上の行」を「ゼロ」行目として扱います。
そのため、CSV ファイルから「データ部の、1 行目から 5 行目まで」を抜き出すコマンドは

$original[0..4]

という記述になります。
(これが一つ目の「members_1_3.csv」になります。)

次は $original[5..9] で、最後は $original[10..14] です。

実際のスクリプトにおいては、数字の部分は変数で記述する必要があります。(後述)

 

< 5 >
こういった「繰り返し作業」の方法はいくつかありますが、私は「while 構文」を用います。
(単なる好みの問題です。)

While 構文は「条件」と「処理」の 2 部から成り、それぞれ ( ) と { } で括ります。

while (繰り返す条件)  {繰り返す作業内容}

 

< 6 >
繰り返した回数を数える「○回目」を、変数「$time」とします。

Powershell の作法(?)に従うなら最初は「ゼロ回目」とすべきなのかもしれませんが、分かりやすくするため、「1」から始めます。

$time = 1

※ この最初の変数定義は繰り返す作業内容に含まれないため、while 構文の直前に実行します。

 

処理を一回終えるたびに、$time に「1」を加算します。
この書き方もいくつかあるのですが、私は「プラスプラス」を用います。
(単なる好みの問題です。)

$time++

※ この加算は、繰り返す作業内容に含まれます。

 

<7>
while 構文の「条件」部は、「$time が  $number 以下なら実行する。」とします。
つまり、「1回目、2 回目、3 回目は実行するが、4 回目は実行しない。」ということです。

while ($time -le $number)

【余談】
確証はありませんが、-le という比較演算子は「リトル」と「イコール」の頭文字を合わせたものと思われます。

 

< 8 >
実際の処理の内容も、$time と $number を利用して記述します。

$original[($time*5-5)..($time*5-1)] | Export-Csv -path members-$time-$number.csv -encoding Unicode

前半(パイプ記号の左側)は、< 4 > の記述を変数を利用して書き換えたものものです。

後半(パイプ記号の右側)で、前半の実行結果を新規の CSV ファイルとして出力しています。

 

<まとめ>
以上の記述をまとめると、以下のようになります。

$original = import-csv members.csv

$whole = ($original).count

$number = [math]::ceiling($whole/5)

$time = 1

while ($time -le $number) {

$original[($time*5-5)..($time*5-1)] | Export-Csv -path members-$time-$number.csv -encoding Unicode

$time++

}

 

 


この内容を「script.ps1」として保存し、

Powershell で実行します。

 

すると意図した通りに分割され、
members-1-3.csv」、
members-2-3.csv」、
members-3-3.csv」の 3 ファイルが出力されました。

めでたしめでたし。

【余談】
Windows 10 (20H2, OS ビルド 19042.746) の Powershell 7.1.1 でも、全く同じ記述で全く同じように動作してくれました。

Powershell 5.1 では、Export-Csv コマンドレットに「-NoTypeInformation」オプションを付けておかないと、
出力されるファイルの先頭に「#TYPE System.Management.Automation.PSCustomObject」という1行が追加されてしまいます。

しかし 7.1.1 では、-NoTypeInformation オプションが無くとも、この1行は追加されませんでした。