PowherShell

Powershell2.0でハマッたCountプロパティの使い方

Powershell2.0でハマッたCountプロパティの使い方

「プロセスが1個しかない場合Get-Processの数を数えようとして失敗します! どうしましょう!?」という相談があったので、対応をまとめました。

状況

 以下のような状況のとのこと。(プロセスは適当に1個のものでテスト)

問題の環境(Powershell2.0)
PS C:\> (Get-Process -ErrorAction 0 | select MainModule | findstr -i "dwm.exe").Count
PS C:\>         ←応答を返さない

最新バージョンで再現するかどうか?

「まずバージョンを確認して、古かったら上げてみよう」。試してみたところそれなりのバージョンならきちんとプロセス数が返ってきます。

Powershell5.1ならきちんと返ってくる
PS C:\> (Get-Process -ErrorAction 0 | select MainModule | findstr -i "dwm.exe").Count
1       ←1を返す

暫定対応

 とはいえ、諸般の事情からすぐバージョン上げられない…ということで調べてみたところ、以下のやり取りがヒット。

 コマンドレットの出力が一つの場合は、配列ではなく単一のオブジェクトとして返却されます。
 その場合出力されるオブジェクトは配列ではないので、countプロパティが存在せず、値を返しません。
 この問題の対処方法はコマンドレットの出力に@()をつけることです。1

 試してみたところうまくいきました。

問題の環境(Powershell2.0)
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が入っていて、そのままシェルが動いてしまうからなかなかバージョン違いに気付かなかったりしますが、なるべく最新のバージョンを用意しておいたほうがよいですね。

参考