Windowsを操作していて、こんな経験がありませんか?

「あのファイルが必要なのに、どこにあるか分からない!」
「ひとつひとつファイルを開いて確認するのは実質無理だ」
結論から述べれば、専用のスクリプトファイルを作成してPowershell上で実行することが手っ取り早いです。
この記事では、こうしたファイル検索に関する問題を解決するために、「現在のディレクトリ以下を再帰的に検索し、指定した文字列を含むファイル一覧を出力するスクリプトの作り方」を解説します。
特定の文字列を含んだファイルを一覧表示する方法
下記のスクリプトをコピペしてください。ファイル名はfindfiles.ps1とします。
# findfiles.ps1
Param(
$arg # 引数
)
try {
$ErrorActionPreference = "Stop" # エラーが発生した際に、強制的に処理を止める
$count = 0
$files = Get-ChildItem -Recurse -File
$totals = ($files | Measure-Object).Count
$files | ForEach-Object {
$count += 1
$per = ($count/$totals)*100
$rtime = $totals - $count
Write-Progress -Activity "Progress: " -Status "$per% Complete" -PercentComplete $per -secondsRemaining $rtime
if (Get-Content $_.FullName | Select-String -Pattern "$arg" -Quiet) {
Write-Host "ファイル名: $($_.FullName)"
}
}
} catch {
Write-Host $_ -ForegroundColor "Red" # catch内の例外情報は、「$_」変数でアクセスできる。
}上記のスクリプトを、PATHが通っているフォルダに保存します。
「PATH通し」に関して詳細を知りたい方は、以下の記事をご参照ください。


使い方
Powershellを立ち上げ、findfiles "文字列"とします。
PS C:\Users\ユーザー名> findfiles "文字列"すると、現在ディレクトリ以下全てを検索し、指定した文字列が記述されているファイル全てを一覧表示します。
使用例
試しに、現在「テスト」フォルダにいるとします。
以下のような構造です。
PS C:\Users\ユーザー名\テスト> tree
.
├── test.txt
├── test01
│ ├── 01
│ │ └── test.txt
│ ├── 02
│ │ └── test.txt
│ ├── 03
│ │ └── test.txt
│ └── test.txt
└── test02
├── 01
│ └── test.txt
├── 02
│ └── test.txt
├── 03
│ └── test.txt
└── test.txt
テストフォルダの中には、2つのフォルダがあり、さらにそれぞれのフォルダの中に3つのフォルダがあります。それら全てのフォルダにtest.txtが存在し、その内2つのファイルには「当たり」と書かれているとします。
このようにサブフォルダがいくつも存在する構造では、ファイルの中身を一つ一つ調べるのも一苦労します。
そこで、ターミナル上でfindfiles "当たり"と入力しましょう。
PS C:\Users\ユーザー名\テスト> findfiles "当たり"
ファイル名: C:\Users\ユーザー名\テスト\test01\test.txt
ファイル名: C:\Users\ユーザー名\テスト\test01\03\test.txtすると、当たりが書かれたファイルパスが一覧表示されました。
Powershellスクリプトの構造解説
このスクリプトがどんな構造になっているのか解説します。
1.引数
まずParam()とは、Powershellで引数を設定するコードです。
ここに引数を設定すると、./hoge.ps1 引数のように引数をスクリプトに渡せるようになります。
Param(
$arg # 引数
)2.例外処理
try{} catch{}は例外処理のコードです。$ErrorActionPreference = "Stop"は、エラーが発生した際に、強制的に処理を止めるための記述です。
try {
$ErrorActionPreference = "Stop" # エラーが発生した際に、強制的に処理を止める
処理
} catch {
例外処理
}3.処理
以下が、「現在ディレクトリ以下を全て検索し、指定した文字列を含んだファイル一覧を出力する方法」のコードです。
$files = Get-ChildItem -Recurse -File
$totals = ($files | Measure-Object).Count
$files | ForEach-Object {
if (Get-Content $_.FullName | Select-String -Pattern "$arg" -Quiet) {
Write-Host "ファイル名: $($_.FullName)"
}
}1つずつ解説します。
$files = Get-ChildItem -Recurse -File- この行では、指定されたディレクトリ内のファイルの一覧を取得しています。
Get-ChildItemコマンドレットを使用してファイル一覧を取得しています。-Recurseフラグは、サブディレクトリ内のファイルも含めて再帰的に検索することを指示します。-Fileフラグは、ファイルのみを取得することを示しています。
- この行では、指定されたディレクトリ内のファイルの一覧を取得しています。
$totals = ($files | Measure-Object).Count- この行では、取得したファイルの数を数えます。
Measure-Objectコマンドレットは、渡されたオブジェクトのプロパティを測定し、統計情報を提供します。.Countは測定されたオブジェクトの数を返します。
- この行では、取得したファイルの数を数えます。
$files | ForEach-Object { ... }- この行では、各ファイルに対して処理を行います。
ForEach-Objectコマンドレットは、配列内の各オブジェクトに対して指定された処理を実行します。- 各ファイルに対して実行される処理は、波括弧
{}内に記述されています。
- この行では、各ファイルに対して処理を行います。
if (Get-Content $_.FullName | Select-String -Pattern "$arg" -Quiet) { ... }- この行では、ファイル内のコンテンツを取得し、特定のパターンに一致するかどうかを確認しています。
Get-Contentコマンドレットは、ファイルの内容を取得します$_は現在のファイルオブジェクトを表しますFullNameプロパティはファイルのフルパスを返しますSelect-Stringコマンドレットは、指定されたパターンに一致する行を選択します-Patternフラグは検索するパターンを指定します-Quietフラグは一致が見つかった場合に真を返します
- この行では、ファイル内のコンテンツを取得し、特定のパターンに一致するかどうかを確認しています。
Write-Host "ファイル名: $($_.FullName)"- この行は、一致したファイルのフルパスを出力します。
Write-Hostコマンドレットは、指定した文字列をコンソールに表示します。$($_.FullName)は、現在のファイルのフルパスを表します。
- この行は、一致したファイルのフルパスを出力します。
一番のポイントは、「Get-Contentコマンドレット」と「Select-Stringコマンドレット」です。
この2つを組み合わせることで、ファイルの中身に特定の文字列があるかどうかを真偽で判定することができます。
4.進捗バー
残りのコードは「処理の進捗」を表示するためのものです。
先程の「テスト」フォルダではファイル数が少ないため、実行しても進捗バーは一瞬で消えますが、数千から数万のファイルを抱えるフォルダで使用する場合は、非常に役立つと思います。
Write-Progress -Activity "Progress: " -Status "$per% Complete" -PercentComplete $per -secondsRemaining $rtime- 処理の進行状況を表示するために
Write-Progressコマンドレットが使用されています。 -Activityフラグは進行状況ウィンドウに表示されるアクティビティ名を指定し、-Statusフラグは進行率のステータスを示します。-PercentCompleteフラグは進行率をパーセンテージで指定し、-secondsRemainingフラグは残りの秒数を指定します。
まとめ
この記事では、現在のディレクトリ以下を再帰的に検索し、指定した文字列を含むファイルの一覧を出力する方法を解説しました。
findfiles.ps1を作成すれば、findfiles "文字列"という簡単なコマンドで指定した文字列が含まれるファイルのパス一覧を取得できます。
特に、数千から数万のファイルを持つプロジェクトでの使用に役立つかと思われます。
