Powershell2.0でハマッたCountプロパティの使い方
「プロセスが1個しかない場合Get-Processの数を数えようとして失敗します! どうしましょう!?」という相談があったので、対応をまとめました。
状況
以下のような状況のとのこと。(プロセスは適当に1個のものでテスト)
PS C:\> (Get-Process -ErrorAction 0 | select MainModule | findstr -i "dwm.exe").Count
PS C:\> ←応答を返さない
最新バージョンで再現するかどうか?
「まずバージョンを確認して、古かったら上げてみよう」。試してみたところそれなりのバージョンならきちんとプロセス数が返ってきます。
PS C:\> (Get-Process -ErrorAction 0 | select MainModule | findstr -i "dwm.exe").Count
1 ←1を返す
暫定対応
とはいえ、諸般の事情からすぐバージョン上げられない…ということで調べてみたところ、以下のやり取りがヒット。
コマンドレットの出力が一つの場合は、配列ではなく単一のオブジェクトとして返却されます。
その場合出力されるオブジェクトは配列ではないので、countプロパティが存在せず、値を返しません。
この問題の対処方法はコマンドレットの出力に@()をつけることです。1
試してみたところうまくいきました。
PS C:\> @(Get-Process -ErrorAction 0 | select MainModule | findstr -i "dwm.exe").Count
1 ←1を返す
PS C:\>
@()のメンバをみてみると、Count = Lengthと定義されています。このように定義されているため、@()をつけてやるとCountが使えるようです。2
PS C:\> Get-Member -InputObject @() -View Extended
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
Powershell2.0依存の話ではないですが、パイプラインを使って大量のオブジェクトを渡してCountすると遅くなるという事象も。こちらはSystem.Linq.Enumerable を使うことで処理時間を短縮させることが可能です(参考文献が詳しい)。3
Windows7環境下だと最初からPowershell2.0が入っていて、そのままシェルが動いてしまうからなかなかバージョン違いに気付かなかったりしますが、なるべく最新のバージョンを用意しておいたほうがよいですね。