この記事は以下のシリーズの18番目の記事です。
この記事でやること
この記事では、PowerShellの Where-Object を使って、条件に合うファイルだけを抽出する方法を整理します。
たとえば、次のような条件です。
| 条件 | 例 |
|---|---|
| 拡張子で絞る |
.txt だけ |
| ファイル名で絞る |
log を含むファイルだけ |
| サイズで絞る | 10MB以上のファイルだけ |
| 更新日時で絞る | 30日以上更新されていないファイルだけ |
| 複数条件で絞る |
.log かつ30日以上前のファイル |
PowerShellでは、Get-ChildItem で対象を集め、Where-Object で条件に合うものだけを残す、という流れがよく使われます。
Get-ChildItem -File |
Where-Object { $_.Extension -eq ".txt" }
まず結論
Where-Object の基本形は次の通りです。
Get-ChildItem -File |
Where-Object { 条件 }
ファイル一覧を条件抽出するときは、次のように考えると分かりやすいです。
集める → 条件で絞る → 必要な項目だけ表示する
代表的な例です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" } |
Select-Object Name, FullName, LastWriteTime
これは、次の意味です。
配下のファイルを集める
拡張子が.logのものだけ残す
Name, FullName, LastWriteTimeだけ表示する
検証用フォルダを作る
まず、検証用フォルダを作成します。
New-Item -ItemType Directory -Path "C:\work\ps-where-test" -Force
Set-Location "C:\work\ps-where-test"
検証用ファイルを作成します。
Set-Content -Path ".\sample1.txt" -Value "text file 1"
Set-Content -Path ".\sample2.txt" -Value "text file 2"
Set-Content -Path ".\app.log" -Value "application log"
Set-Content -Path ".\error.log" -Value "error log"
Set-Content -Path ".\data.csv" -Value "id,name"
サイズ確認用に、少し大きめのファイルも作ります。
"1234567890" * 1000 | Set-Content -Path ".\large.txt"
サブフォルダも作成します。
New-Item -ItemType Directory -Path ".\sub" -Force
Set-Content -Path ".\sub\subsample.txt" -Value "sub folder text"
Set-Content -Path ".\sub\sublog.log" -Value "sub folder log"
確認します。
Get-ChildItem -Recurse
Where-Objectの基本形
Where-Object は、条件に合うものだけを残すコマンドです。
Get-ChildItem -File |
Where-Object { $_.Extension -eq ".txt" }
$_ は、パイプラインで渡されてきた現在の1件を表します。
つまり、次の意味です。
現在処理中のファイルのExtensionが.txtなら残す
よく使うプロパティは次の通りです。
| プロパティ | 内容 |
|---|---|
| Name | ファイル名 |
| FullName | フルパス |
| Extension | 拡張子 |
| Length | ファイルサイズ |
| CreationTime | 作成日時 |
| LastWriteTime | 更新日時 |
確認したい場合は、まず Select-Object で表示すると分かりやすいです。
Get-ChildItem -File |
Select-Object Name, Extension, Length, LastWriteTime
比較演算子の基本
Where-Object では、条件を書くために比較演算子を使います。
| 演算子 | 意味 | 例 |
|---|---|---|
-eq |
等しい | $_.Extension -eq ".txt" |
-ne |
等しくない | $_.Extension -ne ".log" |
-gt |
より大きい | $_.Length -gt 1KB |
-lt |
より小さい | $_.Length -lt 1KB |
-ge |
以上 | $_.Length -ge 1KB |
-le |
以下 | $_.Length -le 1KB |
-like |
ワイルドカード一致 | $_.Name -like "*log*" |
-notlike |
ワイルドカード不一致 | $_.Name -notlike "*tmp*" |
-match |
正規表現一致 | $_.Name -match "error" |
PowerShellでは、比較演算子に = ではなく -eq などを使います。
拡張子で絞る
.txt ファイルだけを抽出します。
Get-ChildItem -File |
Where-Object { $_.Extension -eq ".txt" }
サブフォルダも含める場合は、-Recurse を使います。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".txt" }
.log ファイルだけを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" }
拡張子が .log ではないものを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -ne ".log" }
ファイル名で絞る
ファイル名に log を含むものを抽出します。
Get-ChildItem -File -Recurse |
Where-Object { $_.Name -like "*log*" }
sample で始まるファイルを抽出します。
Get-ChildItem -File -Recurse |
Where-Object { $_.Name -like "sample*" }
error を含むファイルを抽出します。
Get-ChildItem -File -Recurse |
Where-Object { $_.Name -like "*error*" }
-like はワイルドカードを使う条件です。
| 条件 | 意味 |
|---|---|
"sample*" |
sampleで始まる |
"*log*" |
logを含む |
"*.txt" |
.txtで終わる |
サイズで絞る
ファイルサイズは Length で確認できます。
Length はバイト単位です。
Get-ChildItem -File |
Select-Object Name, Length
1KBより大きいファイルを抽出します。
Get-ChildItem -File -Recurse |
Where-Object { $_.Length -gt 1KB }
10MB以上のファイルを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Length -ge 10MB }
100MBより小さいファイルを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Length -lt 100MB }
表示するときは、MBに変換すると見やすくなります。
Get-ChildItem -File -Recurse |
Where-Object { $_.Length -ge 1KB } |
Select-Object Name,
FullName,
@{Name="SizeKB"; Expression={[math]::Round($_.Length / 1KB, 2)}}
更新日時で絞る
更新日時は LastWriteTime で確認できます。
Get-ChildItem -File |
Select-Object Name, LastWriteTime
30日以上更新されていないファイルを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) }
7日以内に更新されたファイルを抽出する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.LastWriteTime -ge (Get-Date).AddDays(-7) }
今日更新されたファイルを抽出する場合は、日付の境界を使うと分かりやすいです。
$today = (Get-Date).Date
$tomorrow = $today.AddDays(1)
Get-ChildItem -File -Recurse |
Where-Object {
$_.LastWriteTime -ge $today -and
$_.LastWriteTime -lt $tomorrow
}
作成日時で絞る
作成日時は CreationTime で確認できます。
Get-ChildItem -File |
Select-Object Name, CreationTime
今月作成されたファイルを抽出する例です。
$firstDayOfThisMonth = Get-Date -Day 1 -Hour 0 -Minute 0 -Second 0
Get-ChildItem -File -Recurse |
Where-Object { $_.CreationTime -ge $firstDayOfThisMonth }
前月以前に作成されたファイルを抽出する場合です。
$firstDayOfThisMonth = Get-Date -Day 1 -Hour 0 -Minute 0 -Second 0
Get-ChildItem -File -Recurse |
Where-Object { $_.CreationTime -lt $firstDayOfThisMonth }
複数条件で絞る
複数条件を組み合わせる場合は、-and や -or を使います。
| 演算子 | 意味 |
|---|---|
-and |
かつ |
-or |
または |
-not |
ではない |
.log かつ30日以上更新されていないファイルを抽出します。
Get-ChildItem -File -Recurse |
Where-Object {
$_.Extension -eq ".log" -and
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
}
.txt または .csv を抽出します。
Get-ChildItem -File -Recurse |
Where-Object {
$_.Extension -eq ".txt" -or
$_.Extension -eq ".csv"
}
名前に error を含み、かつサイズが1KBより大きいファイルを抽出します。
Get-ChildItem -File -Recurse |
Where-Object {
$_.Name -like "*error*" -and
$_.Length -gt 1KB
}
複数条件は、改行して書くと読みやすくなります。
配列に含まれるかで絞る
複数の拡張子を扱う場合は、配列と -in を使うと読みやすいです。
$extensions = @(".txt", ".csv", ".log")
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -in $extensions }
逆に、指定した拡張子以外を抽出する場合は -notin を使います。
$extensions = @(".tmp", ".bak")
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -notin $extensions }
条件が増える場合は、-or を何度も書くより、配列にした方が管理しやすいです。
条件抽出してCSVに出力する
Where-Object は、Export-Csv と組み合わせると実務で使いやすくなります。
30日以上更新されていないファイルをCSVに出力します。
Get-ChildItem -File -Recurse |
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
Select-Object Name, FullName, Extension, Length, LastWriteTime |
Export-Csv -Path ".\old_files.csv" -NoTypeInformation -Encoding UTF8
サイズをMB表示にしてCSV出力する場合です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Length -ge 10MB } |
Select-Object Name,
FullName,
Extension,
@{Name="SizeMB"; Expression={[math]::Round($_.Length / 1MB, 2)}},
LastWriteTime |
Export-Csv -Path ".\large_files.csv" -NoTypeInformation -Encoding UTF8
削除や移動をする前に、まずCSVで一覧化して確認すると安全です。
削除前は必ず-WhatIfで確認する
条件抽出したファイルを削除することもできます。
ただし、いきなり削除するのは危険です。
悪い例です。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" } |
Remove-Item
まずは -WhatIf を付けます。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" } |
Remove-Item -WhatIf
-WhatIf を付けると、実際には削除せず、「何が削除対象になるか」を確認できます。
問題なければ、-WhatIf を外します。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" } |
Remove-Item
条件抽出と削除を組み合わせる場合は、必ず事前確認する習慣をつけた方が安全です。
foreachと組み合わせる
条件抽出したファイルを1件ずつ処理したい場合は、foreach と組み合わせると分かりやすいです。
$files = Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".txt" }
foreach ($file in $files) {
Write-Host "処理対象: $($file.FullName)"
}
コピーやログ出力など、1件ごとに複数行の処理をしたい場合は、foreach が扱いやすいです。
$backupDir = ".\backup"
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
$files = Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".txt" }
foreach ($file in $files) {
$destPath = Join-Path $backupDir $file.Name
Copy-Item -Path $file.FullName -Destination $destPath -Force
}
実務で使いやすいテンプレート
条件に合うファイルを抽出してCSV出力するテンプレートです。
$targetDir = Join-Path $PSScriptRoot "target"
$outputDir = Join-Path $PSScriptRoot "output"
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
$today = Get-Date -Format "yyyyMMdd"
$outputPath = Join-Path $outputDir "filtered_files_$today.csv"
$extensions = @(".log", ".txt")
Get-ChildItem -Path $targetDir -File -Recurse |
Where-Object {
$_.Extension -in $extensions -and
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
} |
Select-Object Name,
FullName,
Extension,
@{Name="SizeMB"; Expression={[math]::Round($_.Length / 1MB, 2)}},
LastWriteTime |
Export-Csv -Path $outputPath -NoTypeInformation -Encoding UTF8
このテンプレートでは、次の条件で抽出しています。
拡張子が.logまたは.txt
かつ
30日以上更新されていない
よくあるつまずき
= を使ってしまう
PowerShellの条件式では、等しいことを表すときに = ではなく -eq を使います。
悪い例です。
Where-Object { $_.Extension = ".txt" }
よい例です。
Where-Object { $_.Extension -eq ".txt" }
拡張子のドットを忘れる
Extension にはドットが含まれます。
$_.Extension -eq ".txt"
次のように .txt ではなく txt と書くと一致しません。
$_.Extension -eq "txt"
$_ の意味が分からなくなる
$_ は、現在処理している1件です。
Get-ChildItem -File |
Where-Object { $_.Extension -eq ".txt" }
この場合、$_ は1つ1つのファイルを表します。
条件が長くなって読みにくい
複数条件は改行します。
Where-Object {
$_.Extension -eq ".log" -and
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
}
削除対象を確認せずにRemove-Itemする
まず -WhatIf を付けます。
Get-ChildItem -File -Recurse |
Where-Object { $_.Extension -eq ".log" } |
Remove-Item -WhatIf
まとめ
この記事では、PowerShellの Where-Object で条件抽出する方法を整理しました。
基本形は次の通りです。
Get-ChildItem -File -Recurse |
Where-Object { 条件 }
よく使う条件は次の通りです。
# 拡張子
$_.Extension -eq ".txt"
# ファイル名
$_.Name -like "*log*"
# サイズ
$_.Length -gt 10MB
# 更新日時
$_.LastWriteTime -lt (Get-Date).AddDays(-30)
# 複数条件
$_.Extension -eq ".log" -and $_.LastWriteTime -lt (Get-Date).AddDays(-30)
PowerShellで条件抽出するときは、次の流れで考えると分かりやすいです。
Get-ChildItemで集める
Where-Objectで絞る
Select-Objectで整える
Export-Csvで出力する
Where-Object を使えるようになると、古いファイルの洗い出し、大きいファイルの確認、特定拡張子の抽出、削除前の確認などが書きやすくなります。
削除や移動と組み合わせる場合は、必ず -WhatIf やCSV出力で対象を確認してから実行するのがおすすめです。