WindowsPowerShellでCSVをDBっぽく扱う

WindowsPowerShell
この記事は約7分で読めます。

CSV に対して特定カラムを抽出したり、条件で抽出したりしたいとき、
Excel でオートフィルタ掛けるのが多分最速の解決方法だと思いますが、
Windows Power Shell の ConvertFrom-CSV を使うと、
まるで DB で SQL を扱うかのように操作できます。
DB ほど速くないですが。

CSV の読み込み

以下のような CSV を使って説明します。一行目はヘッダです。

●test.csv

名前,メーカー,価格
バーチャルボーイ,任天堂,15000
メガジェット,SEGA,15000
光速船,ATARI,54800
ドリームキャスト,SEGA,29900
PCエンジンスーパーグラフィックス,NEC,39800
サテラビュー,任天堂,18000

まずCSVファイルを変数に読み込みます。(エンコードはString)

$CSV_DATA = Get-Content -Path test.csv -Encoding String

読み込んだCSVをpowershellオブジェクトに変換します。

$CSV_DATA = $CSV_DATA | ConvertFrom-Csv

ここまではパイプラインで1行で書けます。

$CSV_DATA = Get-Content -Path test.csv -Encoding String | ConvertFrom-Csv

ここで変数 $CSV_DATA を参照すると以下の結果になります。

 名前                       メーカー      価格
 ----                      --------  ----------
 バーチャルボーイ              任天堂     15000
 メガジェット                  SEGA      15000
 光速船                     ATARI     54800
 ドリームキャスト               SEGA      29900
 PCエンジンスーパーグラフィックス  NEC       39800
 サテラビュー                 任天堂     18000

それぞれの情報は変数名(配列)にドットでアクセス出来るようになります。

$CSV_DATA[0].名前

【結果】
バーチャルボーイ

オブジェクトの操作

? ( = Where-Object )による条件式を使ってフィルタすることもできます。

$CSV_DATA | ? { $_.名前 -eq "ドリームキャスト" }

【結果】

名前                       メーカー      価格
----                      --------  ----------
ドリームキャスト               SEGA      29900

フィルタした情報を修正することすらできます。

 $( $CSV_DATA | ? { $_.名前 -eq "ドリームキャスト" } ).価格 = 9900

【結果】

名前                       メーカー      価格
----                      --------  ----------
バーチャルボーイ              任天堂     15000
メガジェット                  SEGA      15000
光速船                     ATARI     54800
ドリームキャスト               SEGA      9900
PCエンジンスーパーグラフィックス  NEC       39800
サテラビュー                 任天堂    18000

これはちょっと式が面倒臭いのですが、$()とするとまず中身が評価、実行されます。
この場合は

$CSV_DATA | ? { $_.名前 -eq "ドリームキャスト" }

が実行され、
名前が「ドリームキャスト」のものをフィルタし、それ以外を削除します。
ですので、$()の結果は「ドリームキャスト」のオブジェクトだけが残る事になります。

複数オブジェクトの操作

次に

$(「ドリームキャスト」のオブジェクト).価格 = 9900

で、
「ドリームキャスト」のオブジェクトの価格プロパティにアクセスし、”9900″という数字を代入します。
結果的に「ドリームキャスト」の価格プロパティが9900に更新されます。

しかし、長々説明しといてなんですが、この方法には問題があり、
フィルタ結果が複数となる場合は使えないのです。
例えば・・・

$( $CSV_DATA | ? { $_.メーカー -eq "任天堂" } ).価格 = 0

全部0になりそうなものですが

【結果】

このオブジェクトにプロパティ '価格' が見つかりません。プロパティが存在し、設定可能なことを確認してください。
発生場所 行:1 文字:40
+ $( $CSV_DATA | ? { $_.メーカー -eq "任天堂" } ). <<<< 価格 = 0
+ CategoryInfo : InvalidOperation: (価格:String) []、RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound

怒られた。

ドリームキャストは結果が1つだけだったから成功していたのですね。

確実に処理するには % ( = ForEach-Object )を使って、
条件に一致した行を1行ずつ ( $_ ) 処理する事になります。

$CSV_DATA | ? { $_.メーカー -eq "任天堂" } | % { $_.価格 = 99999 }

【結果】


名前                       メーカー      価格
----                      --------  ----------
バーチャルボーイ              任天堂     99999
メガジェット                  SEGA      15000
光速船                     ATARI     54800
ドリームキャスト               SEGA      9900
PCエンジンスーパーグラフィックス  NEC       39800
サテラビュー                 任天堂    99999

終わりに

「ConvertFrom-CSV」コマンドレットを使うと、CSV を DB みたいに使うことが出来ます。
オブジェクト化することによって複数の CSV を読み込んで連結とか、マッチング処理とか、集計・・みたいなバッチ処理とかも自在に書けます。

ディスクアクセスは最初の読み込みと、
最後の出力(ConvertTo-CSV で CSV に戻せる)だけです。

これは最近CSVを扱うことが増えたので、備忘録として書いたんだからね!
私はプログラマーでもDB使いでもないから細かいことはわからないんだからね!
タイトルとURLをコピーしました