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
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
……以下省略……
* 「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コマンドがパイプから受け取るのは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コマンドでは自動的に、その配列に含まれるオブジェクトについて表示してくれる*。
* 例えば、ファイルとサブディレクトリが含まれるディレクトリで「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
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
テキスト・ベースである既存コマンドを実行した場合、その結果は文字列オブジェクトの配列となる。この場合、当然ながら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
■グループ化: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
……以下省略……
この結果を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}
……以下省略……
この結果から、その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}
「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
Copyright© Digital Advantage Corp. All Rights Reserved.