PowerShellで頻繁に使用するパイプライン処理は「フィルタ」を利用すればシンプルに記述できる。フィルタを定義するにはfilterキーワードを使用する。
対象ソフトウェア:Windows PowerShell
PowerShellでは、パイプライン経由で渡されたオブジェクトに対して何らかの処理を施すことで、必要なデータだけを取り出したり、順番に処理したり、あるいは出力形式を整形したりといった操作を行うことがよくある(別稿「Windows PowerShellコマンド&スクリプティング入門」を参照)。例えば、カレントフォルダから最終更新日が6カ月以上前のファイルだけを取得したければ、次のように記述する。
PS C:\WINDOWS> Get-ChildItem | Where-Object {$_.LastWriteTime -lt (Get-Date).AddMonths(-6)}
ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2007/06/07 6:18 ADDINS
d---- 2007/06/08 20:31 AppPatch
d---- 2007/06/07 6:18 Config
d---- 2007/06/07 6:18 Connection Wizard
d---- 2007/06/07 6:18 Cursors
d---- 2007/06/08 18:44 Debug
……(以下省略)……
Where-Objectコマンドレットは、パイプ経由で渡された入力オブジェクトを特定の条件式(フィルタスクリプト)で比較し、合致したオブジェクトのみを出力するものだ。
もっとも、この程度のコードで記述できてしまうとはいえ、頻繁に利用する条件であれば(あるいは条件自体が複雑にもなれば)、これを毎回タイプするのは面倒だ。
そこで登場するのが「フィルタ(Filter)」である。フィルタとは、パイプライン上のオブジェクトを処理するための関数(Function)の一種であると考えればよい。よく利用するパイプライン処理をフィルタとして外部化しておけば、同じようなコードを繰り返し記述する必要がなくなるので、コマンドを簡潔に記述できる。
PowerShellを利用するには、あらかじめシステムにユーザー自身がインストールしておく必要があります。具体的なインストール方法については「PowerShellをインストールする」を参照してください。
フィルタの例として、ここでは、「パイプライン経由で受け取ったオブジェクト(ファイル情報)から、最終更新日が$span月以上前のもののみを取り出す」Get-Oldフィルタを定義してみよう。
まずはテキストエディタ(メモ帳でも何でもよい)を開き、以下のコードを入力してほしい。なお「#〜」で始まる行は、コードの意味を解説するためのコメント部分なので、省略してもよい。コメントにはスクリプトの簡単な説明を入れておいた。
※ファイル:profile.ps1
# 引数$spanは何カ月以上前のファイルを抽出するかを表す値(デフォルトは6カ月前)
filter Get-Old($span=6) {
if($_.LastWriteTime -lt (Get-Date).AddMonths($span * -1)){return $_}
}
フィルタを定義する構文は、(functionキーワードではなく)filterキーワードを利用することを除けば、関数を定義する場合とほとんど同様である。
filter フィルタ名 [(引数[=デフォルト値],...)] {
フィルタの本体
[return 戻り値]
}
ただしfilterブロックの中では、パイプライン経由で渡されたオブジェクトを自動変数「$_」を使って参照できる点に注目していただきたい。ここでは、この自動変数$_を介して、渡されたオブジェクトのLastWriteTimeプロパティ(最終更新日時)を参照し、その値が$span月前よりも古ければ(「-lt」=「less than」。「以下」という意味)、オブジェクト自身を返している。これによって、最終更新日時が$span月以上前のオブジェクトだけをフィルタリングしているわけだ。
なお、このようなフィルタをPowerShell起動のたびに手動で実行するのは現実的ではない。そこで、ここではフィルタをプロファイル*1として登録しておくことにしよう。プロファイルとして登録するには、ファイル名を「profile.ps1」とし、保存先のフォルダは「C:\WINDOWS\SYSTEM32\windowspowershell\v1.0」とする必要がある(ユーザー単位にプロファイルを区別したければ、「C:\Documents and Settings\<ユーザー名>\My Documents\WindowsPowerShell」フォルダでもよい)。
*1 プロファイルとは、起動時に決められたスクリプトを読み込み、実行するための機能のこと。詳細は別稿「PowerShellコマンド&スクリプティング入門(後編)」やTech TIPS「PowerShellのプロンプト文字列をカスタマイズする」も参照。
すでにprofile.ps1が存在する場合には、元のコードを削除せずに、ファイルの末尾に上記リストの内容を追加する。
繰り返しであるが、profile.ps1の内容はPowerShellの起動時に自動的に呼び出されるので、フィルタ登録のための特別な手続きは必要ない。新たなシェルを起動したうえで、試しに以下のようなコードを実行してみよう。
Get-ChildItem | Get-Old
これによって、冒頭で紹介した例と同様の結果が得られれば成功だ。ここでは引数を省略しているが、もしも最終更新日が10カ月以上前のファイルのみを抽出したい場合には、以下のように記述すればよい。
Get-ChildItem | Get-Old(10)
実は、フィルタによるパイプライン処理は、普通の関数(Function)で書き直すこと「も」可能である。以下は、先のGet-Oldフィルタを関数で書き換えたものだ。
function Get-OldFunc($span=6) {
# 空の配列を定義
$result = @()
# パイプライン経由で受け取ったオブジェクト配列$inputを順に処理
foreach($d in $input){
# 最終更新日が$spanカ月前であるオブジェクトのみを配列$resultに追加
if($d.LastWriteTime -lt (Get-Date).AddMonths($span * -1)){
$result += $d
}
}
# 最終的に生成された配列を戻り値として返す
return $result
}
フィルタと関数とが大きく異なる点は、フィルタが「パイプライン経由で渡される個々のオブジェクトに対して、複数回実行される」のに対し、関数は「パイプライン経由で渡されたオブジェクト群に対して、まとめて1回だけ実行される」という点だ。
従って関数ではパイプライン経由で渡されるオブジェクト個々にアクセスする自動変数$_も利用できない。代わりに、パイプラインから渡されたオブジェクト群にアクセスする特殊な変数「$input」を使用する必要がある。よって、関数では必ず反復処理を記述しなければならず、フィルタよりも記述が冗長になりやすい。
また、構文的な違いだけではない。このような処理方法の違いから、一般的には、関数の方がフィルタよりも多くのメモリを必要とする。半面、一括してオブジェクトを取得するため、処理の内容によっては、関数の方が高速に動作する可能性がある。
ささいな違いでもあり、通常の用途ではほとんど気にする必要はないが、処理の重いスクリプトを記述する場合には、この違いを意識しながら両者を使い分けるとよいだろう。
■この記事と関連性の高い別の記事
Copyright© Digital Advantage Corp. All Rights Reserved.