概要
- PowerShell は、.NET Framework を基盤にしたコマンドラインシェルであり、かつスクリプト環境である
- コマンドの出力結果をフィルタするときは、grep ではなく、Where-Object コマンドレットを使う
- ヘルプに情報が充実しているので、help コマンドなどで調べていれば、何となく使い方はわかりそう
コマンドラインシェルとして使ってみる
コマンドラインシェルとして見た場合、コマンドの実行結果の実体は .NET Framework のオブジェクトであり、その点で従来のシェル(cmd.exe やUnix のシェルなど)と大きく異なる。
ためしにdir コマンドを使ってみる。すると PowerShell でも、次のように使うことができる。
PS C:\Users\daisuke> dir ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\Users\daisuke Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2009/03/26 20:32 .nbi d---- 2009/03/26 20:35 .netbeans …略…
出力様式がこれまでとは違うけど、それはまあ置いておくとして、更に PowerShell では、次のような書き方が可能になる。
PS C:\Users\daisuke> (dir).GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array
「(dir)」の次のピリオドは、オブジェクトのメソッドやフィールドにアクセスするための構文。Java や C# をはじめとする各種言語と同様。
つまり、この表現は、dir コマンドが返してきたオブジェクトのGetType メソッドを実行せよという意味で、このように、PowerShell では、コマンドの結果そのものをオブジェクトとして扱っている。
なお、GetType メソッドは、そのオブジェクトの型を取得するためのものであって、.NET Framework の基底クラスである System.Object クラスから継承されているため、どのコマンドの結果に対しても使うことができる、と思う。
ところで上記の例では、dir コマンドが返してきたオブジェクトの型は、System.Array 型らしいことがわかる。どうやら配列らしいので、それなら添字を使うことで各要素にアクセスできるような気がするけど、実際できてしまう。
PS C:\Users\daisuke> (dir)[0] ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\Users\daisuke Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2009/03/26 20:32 .nbi PS C:\Users\daisuke> (dir)[0].GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True DirectoryInfo System.IO.FileSystemInfo
この配列の最初の要素の型は System.IO.FileSystemInfo のようだ。
当然、System.IO.FileSystemInfo クラス固有のメソッドやフィールドを使うこともできる。例えば、ファイルに最後にアクセスした時刻を取得するための LastAccessTime プロパティを得るには、
PS C:\Users\daisuke> (dir)[0].LastAccessTime 2009年3月26日 20:32:07
このような感じ。
コマンドレットとは
実は PowerShell で使った「dir」コマンドはエイリアスで、実体は「Get-ChildItem」という長ったらしいコマンドになる。
PS C:\Users\daisuke> alias dir CommandType Name Definition ----------- ---- ---------- Alias dir Get-ChildItem PS C:\Users\daisuke> Get-ChildItem ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\Users\daisuke Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2009/03/26 20:32 .nbi
こういう「Get-ChildItem」みたいなコマンドをコマンドレットと呼ぶ。PowerShell の組み込みコマンド的な位置づけであるようだ。
「dir」以外にも、「ls」も、「Get-ChildItem」のエイリアスになっている。引数なしで「alias」コマンドを実行して全てのエイリアスを出力してみると、cmd.exe や Unix 系のコマンドの有名なものは、コマンドレットのエイリアスとして準備されていることが分かる。
そのため、コマンドレットを覚えなくてもある程度の作業はなんとなくできる。
grep は使わない
従来のシェルで複雑な処理を行うために使われていたパイプラインは、 PowerShell にも残っている。
パイプラインを使う例として、たとえばカレントディレクトリのファイルのうち、最後にアクセスされた日時が2010年3月のものだけ画面に出力して、さらにテキストファイルとして保存する、というタスクを考える。
$ ls -l | grep '\b2010-03-[0-3][0-9]\b' | tee output
その場合、Unix シェルでは、だいたい上のような使い方をすることになると思う。ファイル以外も出てくるとか、まあ細かいことは置いておくとして。
これを PowerShell で書くと、
PS C:\Users\daisuke> Get-ChildItem | Where-Object { [DateTime]'2010-03-01' -le $_.LastAccessTime -and $_.LastAccessTime -lt [DateTime]'2010-04-01' } | Tee-Object output
こんな感じになる。
構造としては全く同じ。ファイル一覧を取得し、絞り込んで、さらにファイルとコンソールに出力する、という3つのステップから成り立っており、それぞれパイプラインでつながれている。
しかし、絞り込み条件の設定方法が全く異なっていて、これまでのシェルは日付テキストを正規表現で比較している一方、PowerShell ではファイルオブジェクトの日付プロパティを -le(以下)、-lt(より小さい)演算子で比較している。そのために使われているのが Where-Object コマンドレット。
Where-Object コマンドレットは、前のコマンドからパイプライン経由で渡されたオブジェクトを引数のスクリプトブロックを使って評価し、該当するオブジェクトだけ、次に渡す。
PowerShell のコマンドの結果はオブジェクトなので、何かしらフィルタするときは Where-Object コマンドレットをヘビーローテーションすることになりそう。
逆に grep は PowerShell 環境下になると使うことはなくなりそうだ。もちろん文字列型のプロパティを正規表現として比較することはあるだろうけど。
ヘルプを参照する
ヘルプは MSDN にもあるのだけど、ちょっと使ってみようというくらいなら、組み込みのヘルプドキュメントが十分役立つ。
help | ヘルプドキュメントのある全ての項目を一覧表示 |
help Get-ChildItem | Get-ChildItem コマンドレットのヘルプ |
help dir | 同上 |
help Get-ChildItem -detailed | より詳しい Get-ChildItem コマンドレットのヘルプ |
help about_operator | 演算子に関するヘルプ Unix でいう「man operator」みたいな位置づけ |
help *operator* | 「operator」を見出し?に含むヘルプ項目を一覧表示 |
ヘルプを読んでみると、全部日本語化されている上に、使用例もたくさんあり、マイクロソフトの意気込みが伝わってくる。
特に about_operator のような、コマンド以外のドキュメントも組み込まれているのは助かる。ちょっと調べ物、といったときにブラウザを開かなくても済む。