特集
» 2006年07月26日 00時00分 公開

特集:Windows PowerShellレビュー(後編):Windows PowerShellのパワーの源は.NETオブジェクト (2/3)

[遠藤孝信,デジタルアドバンテージ]

オブジェクトが流れるパイプ

 UNIXのシェルやコマンド・プロンプトではパイプ(「|」、「パイプライン」とも呼ばれる)を使うことにより、一時ファイルを作成することなく複数の処理を一度に実行することができる。特にUNIXでは、パイプの使用を前提として、各コマンドはシンプルかつ単機能に設計されており、パイプの使い方が作業効率にも大きく影響するといえるだろう。

 同様にPowerShellでもパイプを多用することになるが、これまでのシェルと違い、パイプを流れるデータはテキスト・データではない。もしテキスト・データであれば以下のような出力は不可能である。

PS C:\proj> dir | sort

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\proj

Mode            LastWriteTime   Length Name
----            -------------   ------ ----
d----    2006/07/12     23:00          bin
-a---    2006/07/12     23:05     2422 Form1.cs
-a---    2006/07/10     17:15     1333 Form1.Designer.cs
-a---    2006/07/10     17:15      344 Form2.cs
d----    2006/07/12     23:00          obj
-a---    2006/07/12     23:06      521 Program.cs
d----    2006/07/12     23:00          Properties
-a---    2006/07/10     17:15     3093 WindowsApplication1.csproj

dirコマンドの結果をソートして表示
dirコマンドの出力が単なるテキスト・データであれば、このように項目ヘッダ(=「Mode LastWriteTime Length Name」の部分)などが正しく表示されることはない。

 PowerShellでは、パイプは、あるコマンドの実行結果であるオブジェクトを別のコマンドに送ることができる。パイプからオブジェクトを受け取ったコマンドはそれを処理して、同様にオブジェクトを出力する。

 上記の例で「dir | sort」の実行結果にさえも項目ヘッダが表示されるのは、dirコマンドからFileInfoオブジェクトの配列を受け取った「sortコマンド」の出力もFileInfoオブジェクトの配列だからである。PowerShellでは、最終的な実行結果がFileInfoオブジェクト(あるいはFileInfoオブジェクトの配列)である場合に、このように項目ヘッダ付きでそれを画面に出力するのだ*

* FileInfoオブジェクトに対してこのような画面出力を行う設定(「ビュー」と呼ばれる)は、PowerShellのインストールされたディレクトリにある「FileSystem.Format.ps1xml」で定義されている。また、ファイルシステム関連以外のオブジェクト(例えばpsコマンドが出力するProcessオブジェクト)などのビューは「DotNetTypes.Format.ps1xml」で定義されている。


 もちろんすべてのCmdletがパイプからオブジェクトを受け取ることができるわけではなく、あらかじめそのように作成されている必要がある。以下ではパイプとともに利用できるいくつかの基本的なCmdletについて紹介する。

■ソート:Sort-Objectコマンド(エイリアス名:sort)

 「Sort-Objectコマンド」は(複数の)オブジェクトを、指定したプロパティの値により並べ替える。以下の例では実行中のプロセスをメモリ使用量(WSプロパティ*)の多い順で表示している。-Descendingオプションは降順に並べ替えるオプションである。

PS C:\proj> ps | sort -Property WS -Descending

Handles  NPM(K)  PM(K)  WS(K) VM(M)  CPU(s)    Id ProcessName
-------  ------  -----  ----- -----  ------    -- -----------
   1839      69  23488  34024   152   44.00  1528 svchost
    615      12  45416  31548   173   27.50   896 powershell
    714      24  28884  27652   526  335.27  2696 WINWORD
    684      19  26744  19268   150  204.52  2744 explorer
    316      11  11764  17800   100    2.48  3188 CCAPP
    171       6   8912  15908    56    0.97   772 conime
    248       7   8856  13228    52   66.45  1484 MsMpEng
    127       5   5848  12536    60    5.75  4780 DivXsm
    655      12  20560  12096   158   16.80  2244 powershell
    227       7  11280  11092    69    2.45   744 NAVAPSVC
……以下省略……

メモリ使用量の多い順にプロセス一覧を表示
sortコマンドでは、-Propertyオプションでソートのキーとなるプロパティを指定する。-Descendingオプションは降順にソートする。

* 「WS」はProcessオブジェクトのWorkingSetプロパティに対する別名で「AliasProperty」と呼ばれるものである。これはtypes.ps1xmlで定義されている。


 この例では省略していないが、sortコマンドではプロパティを指定するオプションである「-Property」の部分の記述は省略できる。

 ちなみに以下の画面は上記のコマンドを実行したときのタスク・マネージャの表示である(同様にメモリ使用量のカラムでソートしている)。上記とほぼ同じ内容になっているのを確認することができる。

上記のコマンド実行時のタスク・マネージャの表示(メモリ使用量でソート)

■選択:Select-Objectコマンド(エイリアス名:select)

 シェルでパイプを使用する目的の1つは、コマンドの実行結果から余計な情報を取り除いていくフィルタリングである。「Select-Objectコマンド」では、パイプで受け取ったオブジェクトから必要なプロパティの情報だけを選択することができる。

 以下ではプロセス一覧をWSプロパティでソートし、その結果からWSプロパティとProcessNameプロパティ(=プロセス名)の部分を抜き出して表示している(ここでは「-Property」の記述を省略している)。

PS C:\proj> ps | sort WS | select WS,ProcessName

                                     WS ProcessName
                                     -- -----------
                                  16384 Idle
                                 274432 System
                                 409600 smss
                                 790528 point32
                                1515520 lsass
                                1880064 wdfmgr
                                2138112 LVPrcSrv
                                2138112 CCEVTMGR
                                2777088 vim
……以下省略……

メモリ使用量とプロセス名のみを表示したプロセス一覧(メモリ使用量でソート)
「selectコマンド」では、パラメータで指定したプロパティのみをオブジェクトから抜き出すことができる。

 ところで、selectコマンドがパイプから受け取るのはProcessオブジェクトの配列であるが、selectコマンドが出力するオブジェクトはどのようなものなのだろうか。これを調べるには先ほども使用したGet-Memberコマンドをパイプで利用するのが便利だ。

PS C:\proj> ps | sort WS | select WS,ProcessName | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
ToString    Method       System.String ToString()
ProcessName NoteProperty System.String ProcessName=Idle
WS          NoteProperty System.Int32 WS=16384

selectコマンドの実行結果のメンバ表示
Get-Memberコマンドは対象となるオブジェクトが配列の場合、その配列の要素のメンバを表示する。ここではselectコマンドの実行結果がPSCustomObject型であることが分かる。

 この例の場合、selectコマンドの出力は配列であるが、Get-Memberコマンドでは自動的に、その配列に含まれるオブジェクトについて表示してくれる*

* 例えば、ファイルとサブディレクトリが含まれるディレクトリで「dir | Get-Member」を実行すれば、FileInfoオブジェクトとDirectoryInfoオブジェクトのメンバ一覧が表示される。


 表示結果から分かるように、selectコマンドが返すオブジェクトはPowerShell独自のPSCustomObject型であり、そこにはパラメータで指定した「ProcessName」や「WS」が含まれている。どうやら動的にメンバを構成する必要がある場合にはPSCustomObject型が使われるようである。表示結果にある「NoteProperty」は動的に追加されたプロパティを示している。

■条件指定:Where-Objectコマンド(エイリアス名:where、「?」)

 selectコマンドは「列」の選択だったが、「行」を選択するのが「Where-Objectコマンド」である。

 このコマンドではパイプから受け取ったオブジェクト(の配列)から指定した条件に当てはまるオブジェクトだけを抽出することができる。このコマンドはよく使用されるためか、あらかじめ「?」という1文字のエイリアス名も付与されている。

 次の例では、実行中のプロセスのうち、メモリの使用量が20Mbytes以上のものだけを抽出している。

PS C:\proj> ps | where { $_.WS -ge 20m }

Handles  NPM(K)  PM(K)  WS(K) VM(M)  CPU(s)    Id ProcessName
-------  ------  -----  ----- -----  ------    -- -----------
    722      19  28740  24152   151  254.16  2744 explorer
    424      12  52396  38976   173   40.70   896 powershell
    594      12  34648  30476   177   19.91  2244 powershell
   1836      69  23560  34148   152   44.19  1528 svchost
    797      26  30656  35980   581  487.73  2696 WINWORD

メモリの使用量が20Mbytes以上のプロセス一覧
「whereコマンド」では、パラメータに記述したスクリプト・ブロックの条件式にマッチするオブジェクトのみを抽出する。「$_」はパイプから送られてくるオブジェクト、「-ge」は「≧」を意味する。

 whereコマンドのパラメータであるスクリプト・ブロック内の式は抽出条件(フィルタと呼ばれる)だ。ここで、「$_」はパイプから送られてくるオブジェクトを示す。これはPowerShellが自動的に設定するシェル変数である。「-ge」はPowerShellにおける比較演算子であり「≧」の意味である*。また、20Mbytesを表すのに「20971520」と書かなくても「20m」と記述できる*

* 比較演算子としては、ほかに「-eq」「-lt」「-gt」「-le」「-ne」、ワイルドカードが使える「-like」、正規表現を指定する「-match」などがある。
* メガを表す「m」以外には、キロを表す「k」とギガを表す「g」が使える。


 次の例では、Windowsに付属のコマンド「ipconfig.exe」の実行結果から文字列「IP Address」を含む行のみを表示している。

PS C:\proj> ipconfig | where { $_ -like "*IP Address*" }
        IP Address. . . . . . . . . . . . : 192.168.1.2
        IP Address. . . . . . . . . . . . : 222.13.15.65
        IP Address. . . . . . . . . . . . : 192.168.0.225

「ipconfigコマンド」の出力結果から「IP Address」を含む行のみを抽出
既存のコマンド(EXEファイル)の実行結果は文字列の配列となる。この場合「$_」は文字列オブジェクトとなる。

 テキスト・ベースである既存コマンドを実行した場合、その結果は文字列オブジェクトの配列となる。この場合、当然ながらCmdletのように特定のプロパティ値のみを処理するということはできないが、配列(=Arrayクラス)や文字列(=Stringクラス)にはたくさんのメソッドが用意されており、テキスト処理にも困ることはないだろう。

■列挙:Foreach-Objectコマンド(エイリアス名:foreach、「%」)

 すでに配列の各要素を列挙するためのforeachステートメントを紹介したが、Cmdletには同じ「foreach」というエイリアス名が付けられた「Foreach-Objectコマンド」も用意されている。

 先ほどは、

foreach ($file in dir *.cs) { $file.Name.ToLower() }

というコマンドを実行したが、これは以下のようにパイプとforeachコマンドを使用しても同様な結果が得られる。

PS C:\proj> dir *.cs | foreach { $_.Name.ToLower() }
form1.cs
form1.designer.cs
form2.cs
program.cs

foreachコマンドによる「*.cs」ファイルのファイル名一覧
パイプとforeachコマンドの組み合わせでは、パイプから送られてくる配列内のオブジェクトを1つずつ処理できる。foreachステートメントと混同しないようにしてほしい。「|」の後にforeachステートメントは記述できないため、この「foreach」はForeach-Objectコマンドのエイリアス名として実行される。

■グループ化:Group-Objectコマンド(エイリアス名:group)

 「Group-Objectコマンド」は、指定したプロパティについて同じ値を持つオブジェクトをグループ化することができる。以下にその使用例を示す。

 PowerShellでは、Get-ChildItemコマンドに対する「ls」や「dir」のように、1つのCmdletに対して複数のエイリアスが用意されている場合がある。ここでは、複数のエイリアス名を持つCmdletの一覧を作成してみよう。

 まず前回でも示したように、すでに定義されているエイリアスの全一覧は「aliasコマンド」で見ることができる。

PS C:\proj> alias

CommandType     Name                          Definition
-----------     ----                          ----------
Alias           ac                            Add-Content
Alias           asnp                          Add-PSSnapin
Alias           clc                           Clear-Content
Alias           cli                           Clear-Item
Alias           clp                           Clear-ItemProperty
Alias           clv                           Clear-Variable
……以下省略……

aliasコマンドによるエイリアス定義の一覧
aliasコマンドの実行結果は、PowerShellが実装しているAliasInfo型のオブジェクトの配列である。そのNameプロパティがエイリアス名、Definitionプロパティがエイリアス元のCmdlet名を表す。

 この結果をDefinitionプロパティでグループ化すれば、同じCmdletを指しているエイリアスが1つにまとめられる。これは次のようになる。

PS C:\proj> alias | group Definition

Count Name                      Group
----- ----                      -----
    1 Add-Content               {ac}
    1 Add-PSSnapin              {asnp}
    1 Clear-Content             {clc}
    1 Clear-Item                {cli}
    1 Clear-ItemProperty        {clp}
    1 Clear-Variable            {clv}
    3 Copy-Item                 {cpi, cp, copy}
    1 Copy-ItemProperty         {cpp}
……以下省略……

エイリアス名ごとにグループ化されたエイリアス一覧
「groupコマンド」ではパラメータで指定したプロパティで、パイプから送られてくるオブジェクトをグループ化できる。ちなみにgroupコマンドの実行結果は、PowerShellで実装されているGroupInfo型のオブジェクトの配列である。

 この結果から、そのCountが2以上のものだけを先ほどのwhereコマンドを使って抜き出すと次のようになる。

PS C:\proj> alias | group Definition | ? { $_.Count -ge 2 }

Count Name                      Group
----- ----                      -----
    3 Copy-Item                 {cpi, cp, copy}
    2 ForEach-Object            {foreach, %}
    3 Get-Content               {gc, cat, type}
    3 Get-ChildItem             {gci, ls, dir}
    3 Get-History               {ghy, h, history}
    2 Get-Location              {gl, pwd}
    2 Get-Process               {gps, ps}
    2 Invoke-History            {ihy, r}
    3 Move-Item                 {mi, mv, move}
    2 New-PSDrive               {ndr, mount}
    6 Remove-Item               {ri, rm, rmdir, del...}
    2 Rename-Item               {rni, ren}
    3 Set-Location              {sl, cd, chdir}
    2 Stop-Process              {spps, kill}
    2 Set-Variable              {sv, set}
    2 Where-Object              {where, ?}
    2 Write-Output              {write, echo}
    2 Clear-Host                {clear, cls}

2つ以上のエイリアス名を持つCmdletの一覧
groupコマンドの実行結果となるオブジェクト(GroupInfoオブジェクト)は、グループ化された項目数を示すCountプロパティを持っている。ここでは「?」(Where-Objectコマンドのエイリアス)により、その値が2以上のものを抽出している。

 「Remove-Itemコマンド」に対するエイリアスは6つもあり、すべてを表示しきれていない。ここでは次のようにして、その一覧を表示してみた。

PS C:\proj> (alias | group Definition | ? { $_.Name -eq "Remove-Item"}).Group

CommandType     Name                            Definition
-----------     ----                            ----------
Alias           ri                              Remove-Item
Alias           rm                              Remove-Item
Alias           rmdir                           Remove-Item
Alias           del                             Remove-Item
Alias           erase                           Remove-Item
Alias           rd                              Remove-Item

Remove-Itemコマンドに対するエイリアス名の一覧
groupコマンドの実行結果であるGroupInfoオブジェクトは、グループ化したオブジェクトをGroupプロパティで取得できるコレクションに格納している。ここではそれを表示している。

  

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。