conf t

インフラエンジニアのメモ

powershellでCSV等の区切り文字があるファイルの数値列の合計を計算する

あるファイルがあり、以下のような中身だったとします。
これの3列目の数値の合計を計算する方法を考えます。

aaaa,bbbb,200,cccc
dddd,eeee,300,ffff
gggg,hhhh,400,iiii
jjjj,kkkk,500,llll

linuxの場合は、awkを使って以下のように計算できます。

# cat test1a.txt | awk -F, '{sum += $3} END {print sum}'
1400

powershellの場合は以下で3列目の合計が計算できます。
Get-Content .\test3.txt | %{$sum += [int]$_.split(",")[2]};$sum
もしくは
Import-Csv .\test3.txt -header(1..4) | Select-Object "3" | %{$sum += [int]$_.3};$sum

パターン1の解説
%はforeachのエイリアスです。
foreach中は$_に一行づつ代入されます。
まず各行は,で区切られているので、splitで分割します。
分割した行は配列として扱われます。
配列でいうとカンマ区切りの3列目はインデックス[2]です。
$sumに$_を加算していき、最後に$sumを表示します。
$_はそのままだと文字列として扱われるため、[int]でキャストしています。
区切り文字がカンマ以外の場合は、splitでその区切り文字を指定すればすぐに応用できます。

コマンドライン上で計算する場合は、このパターン1のほうが覚えやすいと思います。

パターン2の解説
Import-CSVコマンドレットを使います。
このコマンドレットはCSV扱うためのものです。
デフォルトだと先頭行が列名として扱われてしまうため、-headerオプションで列名を作成しています。
select-object以降を消して実行すると以下の結果になります。
列ごとにオブジェクト化されるようです。

PS E:\work> Import-Csv .\test3.txt -header(1..4)

1                             2                             3                             4
-                             -                             -                             -
aaaa                          bbbb                          200                           cccc
dddd                          eeee                          300                           ffff
gggg                          hhhh                          400                           iiii
jjjj                          kkkk                          500                           llll

Select-Objectで列名3を取得しています。

$_.3で計算しているのは、以下のためです。

%{$_ | GetMember}の結果
Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
3           NoteProperty System.String 3=200

$_.Get-Tyep()の結果
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

つまり$_のままだとPSCustomObjectとして扱われるため、この中のプロパティである3に
実際の数値がString型で格納されているため参照しているわけです。

パターン2で使用するimport-csvはすべてのカラムをオブジェクトに入れてくれるので powershellスクリプトなどで、どのカラムも計算対象・処理対象になる等の場合に使用する場合は便利だと思います。
コマンドライン上である列の値の合計だけ出したいときはパターン1の単純にsplitで区切るやり方が簡単かと思います。

追記(6/21)
サクラエディタ正規表現を用いた方法も考えました。
monaski.hatenablog.com