前回までのプログラムで、検索するファイルの期間を設定するパターンをいろいろ作成してみました。だいたいのパターンは把握できたと思います。さらにいろいろな期間をオプションとして設定したいのであれば、前回までのプログラムを参考にバリエーションを増やすことはそう難しいことではありません。

今回は別のタイプのオプションを加えたいと思います。

前回まで作成したGet-FileInformation.ps1では、オプションパラメータTodayやThisWeekなどの期間を指定できるようにしました。ただ、常にカレントディレクトリ以下のすべてのディレクトリ(フォルダ)を検索しますので、目的によってはかえって無駄に情報を表示することになります。むしろ邪魔になることもあります。

そこで、今回は、スイッチパラメータを指定して、カレントディレクトリだけ検索、サブディレクトリも検索、と切り替えられるようにしてみます。

仕様としては、Get-FileInformation.ps1実行時に-rオプション(recurseのr)を指定したらサブディレクトリも検索、-rの指定がなければカレントディレクトリだけ検索するとします。

-rと入力しても、-recurseと入力してもいいように、最初の2文字-rだけを判定し、3文字目以降はあってもなくてもいいとします。(最初が-rであれば-raaでも-r1234でもOK)

パターンマッチングの基本

パターンマッチングとは、文字列のパターンを比較し、条件に合っているか、条件に合わないかを判定することです。たとえば、Get-FileInfomation.ps1のswitch構文で"Today"、"Yesterday"などの文字列をチェックしているのも、パターンマッチングの一種と言えます。ただし、今まで作成したswitch構文では、単純に同じ文字列かどうかを判定するだけです。

PowerShellでは、より本格的なパターンマッチングを行う手段として、-likeと-matchという演算子があります。

-likeの基本的な使い方は、以下の式になります。

比較対象となる文字列 -like  パターン指定文字列

「比較対象文字列」が「パターン指定文字列」が意味するパターンに適合すれば、この式の値が真(true)になります。適合しなければ偽(false)になります。-match演算子の場合も基本的には同じ使い方です。ただし、パターン指定文字列を記述するとき、-likeの場合には、ワイルドカードと呼ぶDOSやWindows流の書式を使いますが、-matchの場合には正規表現と呼ぶUNIX流の書式を使います。-likeの方がシンプルで簡単に使いこなせるようになりますが、-matchの方がより複雑で細かなパターン指定が可能です。

今回は、簡単に使用できるワイルドカードの-likeを解説します。

ワイルドカードで使用する特殊文字は2つ、アスタリスク「*」とクエスチョンマーク「?」です。必ず半角文字で指定します。アスタリスクは「0文字以上の任意の文字列」を意味します。「?」は「任意の1文字」を意味します。

たとえば、「a?b」は、「aとbの間に任意の1文字がある」ことを意味します。abbやa5bは適合しますが、abはaとbの間に1文字ありませんので適合しません。またa12bはaとbの間に2文字ありますので、適合しません。

「a*b」は、「aとbの間に0文字以上の任意の文字列がある」ことを意味します。注意が必要なのは、1文字以上ではなく、0文字以上であることです。たとえば、「ab」はaとbの間が0文字ですので適合します。a1bもa123bもa123456bも適合します。しかし、a123bcは、文字列の最後がcですので適合しません。「a*b*」(aで始まって任意の文字列があってbがあって任意の文字列)とすれば適合します。

ワイルドカードの使い方がわかれば、-rで始まるスイッチパラメータかどうかを判定するパターンが「-r*」であることがわかります。

実際に-likeで試してみましょう。以下のプログラムを入力して実行してみてください。

if ("-recurse" -like "-r*")
{
    Write-Output "適合せり!"
} else {
    Write-Output "適合せず!"
}

-like演算子を使って、ワイルドカードのパターンマッチングを試してみます。

「適合せり!」と表示されるはずです。"-recurse"の部分や"-r*"の文字列部分を、いろいろ変更して実行し、試して下さい。

否定するマッチ演算子

-likeや-matchによるマッチングの排他演算子として-nolikeや-nomatchがあります。たとえば、「-nolike "ab*"」は、「ab*に該当しない文字列」を意味します。

switch構文の拡張

switch構文の基本的な使い方を復習しましょう。構文は以下の通りです。

switch (比較される値)
{
    値1  {
        比較される値が値1だったときの処理
    }
    値2  {
        比較される値が値2だったときの処理
    }
        :
    default  {
        比較される値がどの値にも一致しなかったときの処理
    }
}

通常、比較される値は、完全に一致するかどうかで判定しますが、実は、PowerShellのswitchにはパターンマッチング機能もあります。それを使えば、-like演算子のようなマッチング処理をできます。

ワイルドカードで判定するためには、switch構文の冒頭を、以下のように記述します。

switch  -wildcard  (比較される値)

正規表現で判定するためには-wildcardの代わりに-regexを指定します。

なお、-wildcardも-regexも、比較される値が文字列でなければ意味のない機能です。比較される値が文字列型以外の時は-wildcardや-regexを指定しても意味をなしません。PowerShellは無視します。

追加するプログラムの概要

今回の-rスイッチパラメータとその機能を追加するための、Get-FileInformation.ps1の変更のポイントは次の通りです。

※ 全プログラムリストは、この記事の末尾に掲載しています。

(1)コマンドラインオプションを検査するforループに入る前に、サブディレクトリも検索するかどうかを保存するフラグ、recurseflagを用意します。その初期値をfalseに設定します(プログラムリストの89行目)。

recurseflagの値がfalse(偽)のままであれば、サブディレクトリを検索しません。

recurseflagの値がtrue(真)になれば、サブディレクトリを検索します。

このrecurseflagのように、処理の分岐点の信号となる変数を、プログラムではフラグ(旗)と呼びます。

87:# $period  開始時間と終了時間を保持する配列変数
88:$helpmessage = $true # ヘルプを表示するかどうかのフラグ(初期値true)
89:$recurseflag = $false  # サブディレクトリも検索するかどうかのフラグ(初期値false)

(2)ワイルドカードを使ったマッチング判定をしますので、switchには-wildcard指定を付けます。(94行目)

92:for ($i = 0; $i -lt $args.length; $i++)
93:{
94: switch -wildcard ($args[$i])
95: {

(3)コマンドラインオプションを判定するforループのswitch構文の中で、コマンドラインパラメータに"-r"で始まる文字列があればrecurseflagの値をtrueにセットします。ここでパターンマッチングのためのワイルドカード記述を使用します。(129行目~131行目)

129:        "-r*" {
130:            $recurseflag = $true
131:        }

(3)Get-ChildItemコマンドレットを実行する段階で、recurseflagがtrueであれば、-Recurseオプション付きで、recurseflagがfalseであれば-Recurseオプションなしで、Get-ChildItemを実行します。(159行目~167行目)

159:    if ($recurseflag -eq $true) {   # -r付き
160:        Get-ChildItem -Recurse | `
161:        Where-Object `
162:        { ($_.LastWriteTime -ge $period[0]) -and ($_.LastWriteTime -    lt $period[1]) }
163:    } else {          # -r なし
164:        Get-ChildItem | `
165:        Where-Object `
166:        { ($_.LastWriteTime -ge $period[0]) -and ($_.LastWriteTime -    lt $period[1]) }
167:    }

(4)ヘルプ表示に-rスイッチパラメータの説明を追加します。(156行目~157行目)

156:    Write-Output ""
157:    Write-Output "-r          -    サブディレクトリも検索。"

Get-FileInformation Yesterdayと実行した例。昨日作成したカレントディレクトリのファイルだけを一覧表示します。

Get-FileInformation Yesterday -r と実行した例。サブディレクトリも含めて検索します。

ファイル情報一覧プログラムのまとめ

さて、まだまだ改良の余地はありますが、ひとまず、Get-FileInformation.ps1のプログラミングは今回で区切りを付けることにします。

Get-FileInformation.ps1では、実行時に指定されたコマンドラインの処理、文字列配列の扱い、日付時刻型オブジェクトの計算、for構文やswitch構文、if構文、そして文字列パターンマッチングなどを使いました。

次回からは、また違ったプログラムを取り上げていきます。

ファイル情報一覧プログラムの全プログラムリスト

1:#ファイル情報一覧プログラム Ver.1.0
2:
3:#「今日」の開始日時と終了日時を算出
4:function TodayPeriod
5:{
6:  $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
7:  $date1
8:  $date1.AddDays(1)
9:}
10:
11:#「昨日」の開始日時と終了日時を算出
12:function YesterdayPeriod
13:{
14:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
15:    $date1.AddDays(-1)
16:    $date1
17:}
18:
19:#「直近1週間」の開始日時と終了日時を算出
20:function LastOneWeekPeriod
21:{
22:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
23:    $date1.AddDays(-6)
24:    $date1.AddDays(1)
25:}
26:
27:#「今週」の開始日時と終了日時を算出
28:function ThisWeekPeriod
29:{
30:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
31:
32:    switch ($date1.DayOfWeek)
33:    {
34:       "Monday" { $date2 = $date1 }
35:       "Tuesday" { $date2 = $date1.AddDays(-1) }
36:       "Wednesday" { $date2 = $date1.AddDays(-2) }
37:       "Thursday" { $date2 = $date1.AddDays(-3) }
38:       "Friday" { $date2 = $date1.AddDays(-4) }
39:       "Saturday" { $date2 = $date1.AddDays(-5) }
40:       "Sunday" { $date2 = $date1.AddDays(-6) }
41:       default {} #処理不要
42:    }
43:    $date2
44:    $date1.AddDays(1)
45:}
46:
47:#「今月」の開始日時と終了日時を算出
48:function ThisMonthPeriod
49:{
50:    Get-Date -Day 1 -Hour 0 -Minute 0 -Second 0
51:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
52:    $date1.AddDays(1)
53:}
54:
55:#「今年」の開始日時と終了日時を算出
56:function ThisYearPeriod
57:{
58:    Get-Date -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0
59:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0
60:    $date1.AddDays(1)
61:}
62:
63:#「この1か月」の開始日時と終了日時を算出
64:function LastOneMonthPeriod
65:{
66:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0 #今日の日付
67:    if ($date1.Month -ne 1)      # 今月が1月でないとき
68:    {
69:        Get-Date  $date1 -Month ($date1.Month - 1)    #月を1つ減じる
70:    }
71:    else
72:    {
73:      Get-Date $date -Year ($date1.Year -1) -Month 12  #前年の12月
74:    }
75:    $date1.AddDays(1)
76:}
77:
78:#「この1年」の開始日時と終了日時を算出
79:function LastOneYearPeriod
80:{
81:    $date1 = Get-Date -Hour 0 -Minute 0 -Second 0   #今日の日付
82:    Get-Date  $date1  -Year ($date1.Year -1)
83:    $date1.AddDays(1)
84:}
85:
86:
87:# $period  開始時間と終了時間を保持する配列変数
88:$helpmessage = $true # ヘルプを表示するかどうかのフラグ
89:$recurseflag = $false  # サブディレクトリも検索するかどうかのフラグ
90:
91:#オプションを確認d
92:for ($i = 0; $i -lt $args.length; $i++)
93:{
94:    switch -wildcard ($args[$i])
95:    {
96:        "Today" {       # 今日のファイル一覧を表示
97:            $helpmessage = $false
98:            $period = TodayPeriod
99:        }
100:        "Yesterday" {   # 昨日のファイルを一覧表示
101:            $helpmessage = $false
102:            $period = YesterdayPeriod
103:        }
104:        "LastOneWeek" { # 1週間のファイルを一覧表示
105:            $helpmessage = $false
106:            $period = LastOneWeekPeriod
107:        }
108:        "ThisWeek" { # 今週のファイルを一覧表示
109:            $helpmessage = $false
110:            $period = ThisWeekPeriod
111:        }
112:        "LastOneMonth" { # 1か月のファイルを一覧表示
113:            $helpmessage = $false
114:            $period = LastOneMonthPeriod
115:        }
116:        "ThisMonth" { # 今月のファイルを一覧表示
117:            $helpmessage = $false
118:            $period = ThisMonthPeriod
119:        }
120:        "LastOneYear" { # 1年のファイルを一覧表示
121:            $helpmessage = $false
122:            $period = LastOneYearPeriod
123:        }
124:        "ThisYear" { # 今年のファイルを一覧表示
125:            $helpmessage = $false
126:            $period = ThisYearPeriod
127:        }
128:
129:        "-r*" {
130:            $recurseflag = $true
131:        }
132:
133:        default {
134:            # 何もしません
135:        }
136:    }
137:}
138:
139:#ヘルプを表示するか、ファイル一覧を実行
140:if ($helpmessage)
141:{
142:    Write-Output "Get-FileInformation Ver.0.2 - ファイル一覧コマンド"
143:    Write-Output ""
144:    Write-Output "使用方法"
145:    Write-Output "Get-FileInformation  <オプション>"
146:    Write-Output ""
147:    Write-Output "オプション一覧"
148:    Write-Output "Today       -    今日のファイルを表示。"
149:    Write-Output "Yesterday   -    昨日のファイルを表示。"
150:    Write-Output "LastOneWeek -    1週間のファイルを表示。"
151:    Write-Output "ThisWeek    -    今週のファイル(月曜日以降)を表示。"
152:    Write-Output "LastOneMonth-    1か月のファイルを表示。"
153:    Write-Output "ThisMonth   -    今月のファイルを表示。"
154:    Write-Output "LastOneYear -    1年のファイルを表示。"
155:    Write-Output "ThisYear    -    今年のファイルを表示。"
156:    Write-Output ""
157:    Write-Output "-r          -    サブディレクトリも検索。"
158:} else {
159:    if ($recurseflag -eq $true) {   # -r付き
160:        Get-ChildItem -Recurse | `
161:        Where-Object `
162:        { ($_.LastWriteTime -ge $period[0]) -and ($_.LastWriteTime -lt $period[1]) }
163:    } else {              # -r なし
164:        Get-ChildItem | `
165:        Where-Object `
166:        { ($_.LastWriteTime -ge $period[0]) -and ($_.LastWriteTime -    lt $period[1]) }
167:    }
168:}