WindowsPowerShellでCSVをDBっぽく扱う

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

以下のような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

怒られた。

確実に処理するには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使いでもないから細かいことはわからないんだからね!

Comments are closed.