前回までのプログラムで、検索するファイルの期間を設定するパターンをいろいろ作成してみました。だいたいのパターンは把握できたと思います。さらにいろいろな期間をオプションとして設定したいのであれば、前回までのプログラムを参考にバリエーションを増やすことはそう難しいことではありません。
今回は別のタイプのオプションを加えたいと思います。
前回まで作成した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 "適合せず!"
}
「適合せり!」と表示されるはずです。"-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.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:}