Batch Updating Last Modified Timestamps for Multiple Files

Table of Contents
はじめに
PowerShell を使用して、フォルダ内のファイルの更新日時を全て同じ日時に変更する手順を紹介します。
サブフォルダ内のファイルも全て処理対象とします。
処理の流れは以下のようになります。
flowchart LR A[Get-ChildItem<br>フォルダ内のファイルを列挙] --> B("|"<br>パイプライン演算子) B --> A B --> C[ForEach-Object<br>各ファイルの処理] C --> B
フォルダ内のファイルを列挙
フォルダ内のファイルを列挙するには Get-ChildItem
を使用します。
サブフォルダも含めてすべてのファイルを取得するには以下のオプションを指定します。
-Recurse
: サブフォルダも含めて再帰的に処理-File
: ファイルのみを取得(ディレクトリは除外)
例)カレントフォルダ内のファイルを列挙
Get-ChildItem -Recurse -File
例)指定したフォルダ(c:\hoge)内のファイルを列挙
Get-ChildItem -Recurse -File c:\hoge
各ファイルの処理
各ファイルの処理には ForEach-Object
を使用します。
ForEach-Object
は基本的にパイプライン |
1 を経由して使うことが前提のコマンドです。
| ForEach-Object {
# ここに処理を書く
# 何行書いてもよい
# $_ はパイプライン内の今処理しているオブジェクト(ファイルやフォルダなど)
$_.Name # 今処理しているオブジェクトの名前を表示
}
- ブロック(
{
と}
の間)内に処理を記述します。 $_
はパイプライン内で「今処理しているオブジェクト」を指します。- ブロック内の最後の式の結果が自動的にプロンプトに出力されます(代入した場合は出力されない)。
たとえば以下の場合、プロセス名の一覧が出力されます。
PS C:\hoge> Get-Process | ForEach-Object { $_.Name }
A5M2
AdobeARM
AggregatorHost
AppActions
armsvc
backgroundTaskHost
backgroundTaskHost
chrome
chrome
・・・
(省略)
・・・
WUDFHost
PS C:\hoge>
更新日時の更新
更新日時を更新するにはオブジェクトの LastWriteTime
を設定します。
例)2020年1月1日 0時0分0秒を設定
| ForEach-Object {
$_.LastWriteTime = [DateTime]'2020-01-01 00:00:00'
}
まとめると
「フォルダ内のファイルを列挙」と「各ファイルの処理」を組み合わせると以下のようになります。
カレントフォルダ内のファイルの更新日時を2020年1月1日 0時0分0秒に更新
Get-ChildItem -Recurse -File | ForEach-Object {
$_.LastWriteTime = [DateTime]'2020-01-01 00:00:00'
}
指定したフォルダ内のファイルの更新日時を2020年1月1日 0時0分0秒に更新
Get-ChildItem -Recurse -File C:\hoge | ForEach-Object {
$_.LastWriteTime = [DateTime]'2020-01-01 00:00:00'
}
コマンドレットや引数の省略名を使用すると以下のように書き換えることもできます。
gci -r -File | % {
$_.LastWriteTime = '2020-01-01 00:00:00'
}
- gci = Get-ChildItem
- -r = -Recurse
- % = ForEach-Object
- 日付は文字列でOK(
PowerShell
がDateTime
型に自動変換してくれます)
ワンライナー
以下のように1行で書くこともできます。
gci -r -File | % { $_.LastWriteTime = '2020-01-01 00:00:00' }
現在日時で更新
現在日時で更新するには Get-Date
コマンドレットを使用します。
gci -r -File | % { $_.LastWriteTime = Get-Date }
おまけ
テスト用にサブフォルダとファイルを PowerShell で生成
# サブフォルダ数とファイル数の設定
$subfolderCount = 3
$filesPerFolder = 5
# サブフォルダを作成(存在しなければ)
1..$subfolderCount | ForEach-Object {
$folderName = "SubFolder_$($_)"
if (-not (Test-Path $folderName)) {
New-Item -ItemType Directory -Path $folderName | Out-Null
}
}
# ルートフォルダにもファイルを作る
$targetFolders = @(".") + (Get-ChildItem -Directory | Select-Object -ExpandProperty Name)
foreach ($folder in $targetFolders) {
1..$filesPerFolder | ForEach-Object {
$fileName = "file_{0}.txt" -f (Get-Random -Minimum 1000 -Maximum 9999)
$filePath = Join-Path $folder $fileName
$content = -join ((65..90) + (97..122) | Get-Random -Count 50 | ForEach-Object {[char]$_})
Set-Content -Path $filePath -Value $content
}
}
ランダムな更新日時を設定する
gci -r -File | % {
$daysOffset = Get-Random -Minimum 0 -Maximum 365
$randomDate = (Get-Date).AddDays(-$daysOffset).AddHours((Get-Random -Minimum 0 -Maximum 24))
$_.LastWriteTime = $randomDate
$_.FullName + " -> " + $randomDate
}
Footnotes
-
パイプラインとは、コマンドレット同士を
|
(パイプ記号)でつなぎ、データを順番に渡して処理する仕組み。cmd
やbash
のパイプラインが文字列を渡すのとは異なり、PowerShell
のパイプラインは .NET オブジェクトを渡すのが特徴になります。↩