[void]をいつも忘れる

最近、結合テストエビデンスをとる必要があり、久しぶりにPowerShellを使っています。テストのエビデンス作成は本当に辛く、コーディングの五倍くらい職務への意気が阻喪される作業なので出来るだけ手早く、間違いのないように遂行したい。あと、他の人も同じ作業が出来るようにしたい。ということで、たとえば、DBのテーブルの内容を取得するために、こんなスクリプトを使っています。大体のイメージとお考えください。


function Get-DataTableByAbcNO($table_name, $abc_no, $con){
$q = "SELECT * FROM $table_name WHERE AbcNO = '$abc_no'"
$dt = New-Object "System.Data.DataTable"
$da = New-Object "System.Data.SqlClient.SqlDataAdapter" ($q, $con)
$da.Fill($dt)
return $dt
}

こんな短いスクリプトなのに、二つも罠に嵌っている! 一つ目は、$da.Fill($dt)です。DataAdapter.Fillはintを返すメソッドで、PowerShellは関数内の関数からの戻り値はすべて出力パイプラインに流すので、このままだと、検索された数がまずGet-DataTableByAbcNOの戻り値となり、その後に検索された行が返ることになります。具体的にいうと、戻り値を変数$retに格納した場合、$ret[0]に検索結果の数が、$ret[1]以降にDataRowが格納されます。これは、$da.Fill($dt)の前に、[void]をおき、[void]$da.Fill($dt)とすることで防げます。
次に、最後のreturn $dtですが、僕の意図としては、DataTableを返したいわけです。しかし、そうはなりません。ここの動きは正直よく理解できていませんが、関数戻り値をGetTypeした結果だけ表にするとこうなります(手元の環境2.0CTPでの結果です)。

戻し方 検索結果の数
return $dt 一件 DataRow
複数件 Object[](実際はDataRowの配列)
return ,$dt 一件 DataTable
複数件 DataTable

つまり、結果が複数件ある場合と、一件しかない場合で型が変わってしまいます。これでは扱いにくいので、配列演算子の,を使って配列の要素にしてしまうことでそれを防いでいます。もしくは、関数を呼び出した側で結果を@()にいれてあげてもいいです。長さが1の配列だとその唯一の要素を返し、2つ以上あれば配列として返すのは知っていたのですが、DataTableは別に配列ではないので、なんでこのような挙動になるのかはよくわかりません……。インデクサがあると配列のように扱われるのかな。こっちが修正版。


function Get-DataTableByAbcNO($table_name, $abc_no, $con){
$q = "SELECT * FROM $table_name WHERE AbcNO = '$abc_no'"
$dt = New-Object "System.Data.DataTable"
$da = New-Object "System.Data.SqlClient.SqlDataAdapter" ($q, $con)
[void]$da.Fill($dt)
return ,$dt
}
上記二つに加えて、関数の呼び出しに()はいらない、むしろ、付けると違う挙動になる、というのがPowerShell十大落とし穴へ確実にランクインするところだと思います。関数宣言を関数呼び出しと見た感じ同じになるようにしたのは間違いだった、みたいなことが確かプログラミング言語Cにも書いてあったし、別におかしくない。
あ、そういえば、僕は『Windows PowerShellクックブック』を持っていたな、と思い「レシピ 15.7 プログラム:SQLデータソースに問い合わせる」参照してみると、[void]に関してはほぼ同じ、returnに関してはこんな感じのスクリプトとなっていました。

# クエリからすべてを返す
$dataSet.Tables | Select-Object -Expand Rows
なるほど。『Windows PowerShellクックブック』はこのレシピでもExcelからでもAccessからでもSQLサーバーからでもデータ取れるように作られたスクリプトが丸々載っていたり、あととにかく異常に扱っている範囲が広い、コマンドレット、スナップインの作り方からいろいろ幅広く扱ってる感じです。特に他の本に比べて、ということだと、III部の管理者タスクがすごく充実してるのでいいのではないかなぁ、と思います。僕は管理者ではないので、付録AのPowerShell 言語と環境など、付録群も簡潔・明瞭にまとまってていいな、と思いました。