前回の成果

作成中のPowerShellスクリプトGet-FileInformation.ps1では、前回、「今日」の範囲...つまり今日の0:00と明日の0:00を算出する関数TodayPeriodまで作成しました(連載第22回参照)。

これを利用して、今日のファイル一覧を表示するようにします。

手法としては、Get-ChildItemコマンドレットで取得したファイル情報を、Where-Objectでフィルタリングします。その際に、自作関数TodayPeriodで算出した日付時刻を使います。

TodayPeriodの計算結果をオブジェクトに代入する

自作関数TodayPeriodの出力は、2つの時刻です。CやC++の関数では、複数の値を返値にすることはできません。PowerShellの場合は出力結果はパイプライン流しますので、複数の値があっても、それが次々とパイプラインに出力されるだけです。これを変数に代入すれば、値の集合...つまり配列...として保存されます。したがって、以下の式でTodayPeriodの計算した結果を、配列オブジェクトtimeに代入できます。

$time = TodayPeriod

代入した2つの値は、$time[0]、$time[1]として参照できます。

PowerShell ISEで前回作成したGet-FileInformation.ps1を開き、timeにTodayPeriodの値を代入する行を追加してGet-FileInformationを実行します。

Get-FileInformationを実行してtimeに代入した後、コマンドラインペインで$time、$time[0]、$time[1]をそれぞれ実行して値を確認します。

今日のファイルを表示

一方、時刻1から時刻2に達するまでの期間のファイルを表示するWhere-Objetフィルタは、以下のようにします。

Where-Object { ($_.LastWriteTime -ge 時刻1) -and ($_.LastWriteTime -lt 時刻2)}

PowerShellでは日付時刻の新旧は値の大小として比較演算できます。「-ge 時刻1」が「」時刻1以降」、「-lt 時刻2」で「時刻2の直前まで」を判断できます。

多くのプログラム言語では数学でおなじみの「>」や「<」を比較演算子に使っていますので、-eqや-ge、-ltなどはわかりにくいかもしれません。PowerShellの比較演算子は以下のようになります。

演算子 意味 Basicの場合
-eq equal(等しい) =
-ne Not Equal(等しくない) <>
-lt Less Than(より小さい) <
-le Less than or Equal(以下) <=
-gt Greater Than(より大きい) >
-ge Greater than or Equal(以上) >=

そこで、今日のファイルを一覧表示するスクリプトは以下のようになります。

1:function TodayPeriod
2:{
3:  $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
4:  $date1
5:  $date1.AddDays(1)
6:}
7:
8:$time = TodayPeriod
9:
10:Get-ChildItem -Recurse | Where-Object { ($_.LastWriteTime -ge $time[0]) -and ($_.LastWriteTime -lt $time[1]) }

※ 行番号は便宜上つけているもので、実際のPowerShellスクリプトには不要です。

1~6行は、前回作成したTodayPeriodそのままです。それを8行目で配列オブジェクトtimeに代入します。10行目で、Get-ChildItemで得たファイル一覧情報から、Where-Objectを使って今日のファイルだけを出力するようにフィルタを掛けています。Get-ChildItemには-Recurseパラメータを指定していますので、サブディレクトリも含めて検索します。

ファイル一覧を表示する行を追加し、Get-FileInformationを実行します。

コマンドライン用パラメータの解釈

Get-FileInformationの仕様としては、コマンドラインでtodayパラメータを指定したときに、今日の日付のファイルを表示するというものでした。この仕様に合わせてGet-FileInformationの枠組みを作ります。

パラメータとしてtodayを指定されたときは、TodayPeriodを使って今日のファイルを表示し、それ以外の時にはヘルプメッセージを表示するようにします。

スクリプトを実行するとき、コマンドライン引数は、配列オブジェクトargsに格納されます。コマンドライン引数を、順番に$args[0]、$args[1]、$args[2]...として参照できます。また、argsは配列オブジェクトなので、プロパティlengthを使ってその個数...つまり引数の個数を参照できます。

args配列の中にtodayの文字列があるかどうか確認し、todayがあれば今日の日付のファイル一覧を表示します。todayの文字列がなければ、ヘルプを表示して終了します。

このプログラムの流れは次のようになります。

1:$helpmessage = true   # ヘルプを表示するかどうかのフラグ
2:
3:for ($i = 0; $i -lt $args.length; $i++)
4:{
5:  switch($args[$i])
6:  {
7:      "today" {
8:          # 今日のファイル一覧を表示
9:          $helpmessage = false
10:     }
11:     default {
12:         # 何もしません
13:     }
14: }
15:}
16:
17:if ($helpmessage)
18:{
19: #ヘルプメッセージを表示
20:}

※ PowerShellスクリプトプログラムでは、各行の#以降の部分はコメントとして扱います。つまり、#以降の部分はプログラムとして実行しません。メモ書きなどに利用します。

ヘルプ表示

1行目は、ヘルプを表示するかどうかの判定基準として変数オブジェクトhelpmessageを作成します。最初、$true(真)を代入しておき、正しいパラメータ(ここではtoday)が指定されたときには9行目で$false(偽)に変更します。

プログラムの最後(17~20行)で、helpmessageの値が$trueのままなら(正しいパラメータがなかったので)ヘルプを表示しします。値が$falseになっていれば(正しい処理がされたはずなので)ヘルプを表示しません。

17行目のif()の()内のhelpmessageの値がtrue(真)かfalse(偽)か判定する条件式は「$helpmessage -eq $true」と記述してもかまいませんが、$helpmessageの値そのものが真偽値なので単に$helpmessageと記述するだけでかまいません。

for、switchについては、次回で解説します。

配列添え字はなぜ0から始まる?


CやC++、PowerShellなどのプログラム言語では、配列の添え字が0から始まります。最初の値はarray[1]ではなく、array[0]となります。最後の値は...たとえば10個の値がある配列だとすると、array[10]ではなくarray[9]となります。

添え字が「○個目」と考えると0から始まるのは非常に不自然に感じますが、この添え字は「順番」ではなく「オフセット」(offset: 基準となる位置からの距離)を意味しています。配列の最初が基準となり、基準からの距離が0となるのは配列の最初の変数、基準からの距離が1となるのは2番目の変数です。

配列添え字が0から始まる理由、納得して頂けたでしょうか?