PowerShellでファイルのタイムスタンプを変更する簡単な方法(touchコマンド代替)

はい、承知いたしました。PowerShellでファイルのタイムスタンプを変更する方法について、Linuxのtouchコマンドの代替として詳細に解説する記事を作成します。約5000語(日本語で1万文字〜1.2万文字程度)というご要望ですが、AIでの正確な文字数調整は難しいため、内容を網羅的かつ詳細に記述することで、十分なボリュームを持たせるよう努めます。


PowerShellでファイルのタイムスタンプを変更する:Linux touchコマンドの強力な代替手段

はじめに

ファイルがいつ作成され、いつ最後に変更され、いつ最後にアクセスされたかを示す「タイムスタンプ」は、ファイル管理、バックアップ、ビルドプロセス、ソフトウェア開発、テストなど、さまざまな場面で非常に重要な情報です。オペレーティングシステムは通常、ファイルの操作(作成、書き込み、読み取りなど)に応じてこれらのタイムスタンプを自動的に更新します。

しかし、時として私たちは意図的にファイルのタイムスタンプを変更したいというニーズに直面します。例えば:

  • バックアップからの復元時: ファイルのオリジナルのタイムスタンプを復元したい。
  • ビルドシステムやMakeファイル: 依存関係を正しく解決するために、ファイルの更新日時を特定の状態に設定したい。
  • ファイルのソートやフィルタリング: 特定の日時を基準にファイルを整理するために、タイムスタンプを調整したい。
  • テストとデバッグ: 特定のシナリオを再現するために、ファイルのタイムスタンプを過去や未来の日付に設定したい。
  • ファイルの存在確認(touchの側面): ファイルが存在しない場合にタイムスタンプを更新することで、ファイルを作成したい。

LinuxやmacOSといったUnix系システムでは、touchコマンドがこのタイムスタンプ変更の主要なツールとして広く知られています。touchコマンドは非常にシンプルでありながら強力で、ファイルの最終更新日時と最終アクセス日時を現在時刻に変更したり、特定の日時を指定したり、存在しないファイルを新しく作成したりすることができます。

では、Windows環境で主に使われる強力なシェル環境であるPowerShellでは、どのようにして同様の操作を行うのでしょうか? PowerShellは.NET Framework/.NET Coreの上に構築されており、ファイルシステムを含むシステムの様々な要素にアクセスするための豊富なコマンドレット(Cmdlet)やクラスを提供しています。touchコマンドのような単一のコマンドは存在しませんが、PowerShellを使うことで、touchコマンドが提供する機能のすべて、そしてそれ以上の柔軟性を持ってファイルのタイムスタンプを操作することが可能です。

この記事では、PowerShellを使用してファイルのタイムスタンプ(作成日時、最終更新日時、最終アクセス日時)を変更する様々な方法を、詳細なコード例を交えながら解説します。Linuxのtouchコマンドの機能をPowerShellでどのように実現するかに焦点を当てつつ、PowerShellならではの強力なファイル操作機能も紹介します。

ファイルのタイムスタンプとは? 3つの種類

ファイルには、通常、以下の3種類のタイムスタンプが関連付けられています。これらのタイムスタンプはファイルシステムの構造の一部として格納され、Windowsを含む多くのOSで利用可能です。

  1. 作成日時 (Creation Time):

    • ファイルが作成された日時を記録します。
    • 通常、ファイルが作成された際に一度設定され、その後はファイルをコピーしたり移動したりしても、元の作成日時が保持されることが多いです(ただし、コピー方法や移動先のファイルシステムによっては新しい作成日時が付与される場合もあります)。
    • ファイルの内容が変更されても、このタイムスタンプは変わりません。
  2. 最終更新日時 (Last Write Time):

    • ファイルの内容が最後に変更された日時を記録します。
    • ファイルにデータを書き込んだり、内容を編集して保存したりすると、このタイムスタンプが更新されます。
    • このタイムスタンプは、ファイルの「内容」がいつ最新の状態になったかを示すため、ビルドシステムやバックアップツールなどでファイルの変更を検出するためによく利用されます。Linuxのtouchコマンドがデフォルトで変更するのは、主にこの最終更新日時です(同時に最終アクセス日時も)。
  3. 最終アクセス日時 (Last Access Time):

    • ファイルの内容が最後に読み込まれた日時を記録します。
    • ファイルを開いて内容を表示したり、プログラムがファイルを読み込んだりすると、このタイムスタンプが更新される「はず」です。
    • しかし、この最終アクセス日時の記録は、特に最近のWindows OSではパフォーマンス上の理由からデフォルトで無効になっているか、または更新が遅延される設定になっていることが多いです。これは、多くのファイルに対する読み取り操作が頻繁に行われる場合に、その都度タイムスタンプを更新するディスクI/Oのオーバーヘッドを避けるためです。したがって、このタイムスタンプは必ずしも信頼できる情報とは限りません。

これらのタイムスタンプは、Windowsのエクスプローラーの詳細表示や、PowerShellのGet-ChildItemコマンドレットなどで確認できます。

“`powershell

例:現在のディレクトリのファイル情報を取得してタイムスタンプを表示

Get-ChildItem | Select-Object Name, CreationTime, LastWriteTime, LastAccessTime
“`

Name CreationTime LastWriteTime LastAccessTime
---- ------------ ------------- --------------
MyFile.txt 2023/10/27 10:00:00 2023/10/27 11:30:00 2023/10/27 12:15:00
Another.log 2023/10/26 09:00:00 2023/10/27 08:10:00 2023/10/27 09:05:00

次に、これらのタイムスタンプをPowerShellでどのように変更できるかを見ていきましょう。

PowerShellでのタイムスタンプ変更の基本:Set-ItemProperty

PowerShellでは、ファイルやフォルダなどの「アイテム」のプロパティを変更するために、Set-ItemPropertyというコマンドレットがよく使われます。Get-ChildItem(またはそのエイリアスdir, ls)で取得されるファイルオブジェクトは、これらのタイムスタンプ情報をプロパティとして持っています。

ファイルオブジェクトの主要なタイムスタンプ関連プロパティは以下の通りです。

  • CreationTime
  • LastWriteTime
  • LastAccessTime

Set-ItemPropertyコマンドレットは、これらのプロパティに新しい日付と時刻の値を設定するために使用できます。基本的な構文は以下のようになります。

powershell
Set-ItemProperty -Path "ファイルのパス" -Name "プロパティ名" -Value "新しい日付時刻"

ここで、-Pathパラメータには変更したいファイルのパスを指定します。-Nameパラメータには変更したいタイムスタンプのプロパティ名(CreationTime, LastWriteTime, LastAccessTimeのいずれか)を指定します。-Valueパラメータには、設定したい新しい日付と時刻を指定します。この値は、PowerShellが認識できる日付/時刻形式の文字列であるか、または[System.DateTime]型のオブジェクトである必要があります。Get-Dateコマンドレットは現在の日時を表す[System.DateTime]オブジェクトを返すため、よく-Value (Get-Date)のように使われます。

では、それぞれのタイムスタンプを変更する具体的な方法を見ていきましょう。

最終更新日時の変更 (LastWriteTime)

最終更新日時は、ファイルのタイムスタンプの中で最も頻繁に変更され、また最も一般的に「ファイルのタイムスタンプ」として参照されるものです。Linuxのtouchコマンドがデフォルトで変更するのも主にこのタイムスタンプです。

例1:単一ファイルの最終更新日時を現在時刻に設定する

これは、Linuxのtouch ファイル名の最も一般的な使い方に相当します。

“`powershell

例:C:\Temp\MyFile.txt の最終更新日時を現在時刻にする

まず、ファイルが存在しない場合は作成(touchの機能代替)

存在する場合はそのままタイムスタンプ変更

$filePath = “C:\Temp\MyFile.txt”

ファイルが存在しない場合は作成(touchのデフォルト挙動)

if (-not (Test-Path $filePath -PathType Leaf)) {
New-Item -Path $filePath -ItemType File | Out-Null # 空のファイルを静かに作成
Write-Host “$filePath を作成しました。”
}

最終更新日時を現在時刻に設定

Set-ItemProperty -Path $filePath -Name LastWriteTime -Value (Get-Date)

Write-Host “$filePath の最終更新日時を現在時刻に変更しました。”

確認

Get-Item $filePath | Select-Object Name, LastWriteTime
“`

このコードでは、まずTest-Pathコマンドレットを使ってファイルが存在するかどうかを確認しています。存在しない場合は、New-Itemコマンドレットで空のファイルを作成しています。これはLinux touchコマンドの「ファイルが存在しない場合は作成する」という重要な機能を再現しています。ファイルが存在する場合は、Set-ItemPropertyLastWriteTime(Get-Date)、つまり現在の日時に設定しています。

例2:単一ファイルの最終更新日時を特定の日時に設定する

特定の日時(過去または未来)に最終更新日時を設定したい場合。

“`powershell

例:C:\Temp\MyFile.txt の最終更新日時を2023年1月1日 10:00:00 に設定

$filePath = “C:\Temp\MyFile.txt”
$newDateTime = “2023-01-01 10:00:00” # 日時文字列

ファイルが存在することを確認 (または作成)

if (-not (Test-Path $filePath -PathType Leaf)) {
# ここでファイル作成のロジックを入れるか、単にエラーとして扱うかは目的に応じる
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

最終更新日時を設定

Set-ItemProperty -Path $filePath -Name LastWriteTime -Value $newDateTime

Write-Host “$filePath の最終更新日時を $newDateTime に変更しました。”

確認

Get-Item $filePath | Select-Object Name, LastWriteTime
“`

-Valueパラメータには、"YYYY-MM-DD HH:mm:ss"のような形式の日時文字列を指定できます。PowerShellはこれを自動的に[System.DateTime]オブジェクトに変換しようとします。より確実に[System.DateTime]オブジェクトを作成して指定することも可能です。

“`powershell

[System.DateTime] オブジェクトを直接作成して指定する

$newDateTimeObject = [System.DateTime]::Parse(“2023-01-01 10:00:00”)
Set-ItemProperty -Path $filePath -Name LastWriteTime -Value $newDateTimeObject

または Get-Date を使って特定の日の0時に設定

$targetDate = Get-Date “2023-01-01”
Set-ItemProperty -Path $filePath -Name LastWriteTime -Value $targetDate
“`

例3:複数のファイルの最終更新日時をまとめて現在時刻に設定する

特定のディレクトリ内の複数のファイルや、特定のパターンに一致するファイルの最終更新日時をまとめて変更したい場合、PowerShellのパイプラインが非常に便利です。

“`powershell

例:カレントディレクトリ内の全ての .txt ファイルの最終更新日時を現在時刻にする

Get-ChildItem *.txt | Set-ItemProperty -Name LastWriteTime -Value (Get-Date)

Write-Host “カレントディレクトリ内の全ての .txt ファイルの最終更新日時を現在時刻に変更しました。”

確認(最初の5件だけ表示)

Get-ChildItem *.txt | Select-Object -First 5 Name, LastWriteTime
“`

Get-ChildItem *.txtで取得された各ファイルオブジェクトはパイプラインを通してSet-ItemPropertyに渡されます。Set-ItemPropertyはパイプラインからの入力を受け付けるように設計されており、-Pathパラメータを指定せずに-Name-Valueを指定するだけで、入力オブジェクト(ここではファイルオブジェクト)の該当プロパティを変更できます。

例4:複数のファイルの最終更新日時を特定の日時に設定する

複数のファイルを特定の日時にまとめて設定する場合も、パイプラインとSet-ItemPropertyを組み合わせます。

“`powershell

例:カレントディレクトリ内の全ての .log ファイルの最終更新日時を2023年3月15日 14:30:00 に設定

$targetDateTime = “2023-03-15 14:30:00”

Get-ChildItem *.log | Set-ItemProperty -Name LastWriteTime -Value $targetDateTime

Write-Host “カレントディレクトリ内の全ての .log ファイルの最終更新日時を $targetDateTime に変更しました。”

確認(最初の5件だけ表示)

Get-ChildItem *.log | Select-Object -First 5 Name, LastWriteTime
“`

パイプライン処理は、大量のファイルに対して一括操作を行う際に非常に効率的です。

作成日時の変更 (CreationTime)

作成日時は、ファイルが最初に作成された日時を記録します。通常、このタイムスタンプが変更されることは少ないですが、バックアップからの復元などでオリジナルの作成日時を復元したい場合に手動で変更することがあります。

作成日時の変更も、Set-ItemPropertyコマンドレットを使って最終更新日時と同様の方法で行えます。

“`powershell

例:C:\Temp\MyFile.txt の作成日時を2023年1月1日 09:00:00 に設定

$filePath = “C:\Temp\MyFile.txt”
$newCreationTime = “2023-01-01 09:00:00”

ファイルが存在することを確認

if (-not (Test-Path $filePath -PathType Leaf)) {
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

作成日時を設定

Set-ItemProperty -Path $filePath -Name CreationTime -Value $newCreationTime

Write-Host “$filePath の作成日時を $newCreationTime に変更しました。”

確認

Get-Item $filePath | Select-Object Name, CreationTime
“`

注意点: 作成日時は、一部のファイルシステム操作(例:NTFSドライブ内でのコピー&ペースト vs. 同じドライブ内での移動)やシステム設定によって挙動が変わることがあります。また、一度設定すると、再度変更するのが難しいケースがあるという情報もあります(ファイルシステムやOSのバージョンに依存する可能性があります)。一般的には、作成日時はファイルの一生を通じて不変であるかのように扱われることが多いため、変更が必要な場合は慎重に行うべきです。

最終アクセス日時の変更 (LastAccessTime)

最終アクセス日時は、ファイルが最後に読み込まれた日時を記録します。これもSet-ItemPropertyを使って変更できます。

“`powershell

例:C:\Temp\MyFile.txt の最終アクセス日時を現在時刻に設定

$filePath = “C:\Temp\MyFile.txt”

ファイルが存在することを確認

if (-not (Test-Path $filePath -PathType Leaf)) {
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

最終アクセス日時を設定

Set-ItemProperty -Path $filePath -Name LastAccessTime -Value (Get-Date)

Write-Host “$filePath の最終アクセス日時を現在時刻に変更しました。”

確認

Get-Item $filePath | Select-Object Name, LastAccessTime
“`

重要な注意点:WindowsにおけるLastAccessTimeの挙動

前述したように、WindowsのNTFSファイルシステムでは、パフォーマンス最適化のために最終アクセス日時の自動更新がデフォルトで無効になっているか、または遅延して更新される設定になっていることが多いです。

  • Windows Vista/Server 2008以降: デフォルトで最終アクセス日時の更新は無効化されています。
  • それ以前のバージョン: アクセス時にタイムスタンプが更新されますが、頻繁な更新はパフォーマンスに影響を与える可能性があります。

この設定はfsutilコマンドで確認・変更できますが、システム全体のパフォーマンスに影響を与えるため、通常はこの設定を変更することは推奨されません

“`cmd

コマンドプロンプトまたはPowerShell (管理者として実行) で実行

現在の設定を確認 (0x3 が推奨設定、無効化と遅延)

fsutil behavior query disablelastaccess

設定を変更 (非推奨!)

fsutil behavior set disablelastaccess 0 # 有効化 (ただし遅延更新)

fsutil behavior set disablelastaccess 1 # 無効化

“`

したがって、Set-ItemPropertyLastAccessTimeを設定することは可能ですが、その後ファイルにアクセスしても、OSが自動的にこのタイムスタンプを更新しない可能性が高いです。つまり、手動で設定した値がそのまま保持され続けるか、またはOSのポリシーに基づいて遅延して更新されることになります。最終アクセス日時は、他のタイムスタンプほど信頼性や実用性が高くない場合があることを理解しておく必要があります。

複数のタイムスタンプを同時に変更する

Set-ItemPropertyコマンドレットは一度に一つのプロパティしか変更できません。もし作成日時と最終更新日時の両方を変更したい場合は、それぞれのSet-ItemPropertyコマンドを続けて実行する必要があります。

“`powershell

例:C:\Temp\MyFile.txt の作成日時と最終更新日時を特定の日時に設定

$filePath = “C:\Temp\MyFile.txt”
$targetDateTime = “2023-12-31 23:59:59”

ファイルが存在することを確認

if (-not (Test-Path $filePath -PathType Leaf)) {
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

作成日時を設定

Set-ItemProperty -Path $filePath -Name CreationTime -Value $targetDateTime

最終更新日時を設定

Set-ItemProperty -Path $filePath -Name LastWriteTime -Value $targetDateTime

Write-Host “$filePath の作成日時と最終更新日時を $targetDateTime に変更しました。”

確認

Get-Item $filePath | Select-Object Name, CreationTime, LastWriteTime
“`

複数のプロパティをまとめて設定したい場合、またはよりプログラム的な操作を行いたい場合は、次に紹介する.NET Framework/.NET Coreのクラスを直接利用する方法が有効です。

.NET Framework/.NET Core クラスを使用したタイムスタンプの変更

PowerShellは.NETの上で動作するため、.NETのクラスやメソッドをスクリプト内で直接利用することができます。ファイルの操作に関しては、System.IO名前空間に強力なクラス群が用意されています。特に、ファイルのタイムスタンプ操作には以下のクラスが役立ちます。

  • [System.IO.FileInfo]:単一のファイルを表すオブジェクト。プロパティを使ってタイムスタンプの取得・設定ができます。
  • [System.IO.File]:静的メソッドを提供し、ファイルパスを指定して直接タイムスタンプを設定できます。

[System.IO.FileInfo] クラスを使用する

Get-ChildItemコマンドレットが返すファイルオブジェクトは、実際には[System.IO.FileInfo]または[System.IO.DirectoryInfo]型のオブジェクトです。したがって、Get-ChildItemで取得したオブジェクトのプロパティを直接変更することで、タイムスタンプを設定できます。これは、Set-ItemPropertyが内部的に行っていることと似ています。

[System.IO.FileInfo]オブジェクトには、以下の設定可能なプロパティがあります。

  • CreationTime
  • LastWriteTime
  • LastAccessTime

これらのプロパティは読み取りだけでなく書き込みも可能なので、直接新しい値を代入することでタイムスタンプを変更できます。

例5:[System.IO.FileInfo] オブジェクトを使って最終更新日時を変更する

“`powershell

例:C:\Temp\MyFile.txt の最終更新日時を現在時刻に設定

$filePath = “C:\Temp\MyFile.txt”

ファイルオブジェクトを取得

ファイルが存在しない場合はエラーになるので注意

Get-Item は -ErrorAction SilentlyContinue でエラーを抑制し、$nullを返す

$file = Get-Item $filePath -ErrorAction SilentlyContinue

if (-not $file) {
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

FileInfoオブジェクトのプロパティに直接代入

$file.LastWriteTime = Get-Date

Write-Host “$filePath の最終更新日時を現在時刻に変更しました (FileInfo)。”

確認

Get-Item $filePath | Select-Object Name, LastWriteTime
“`

この方法の利点は、ファイルオブジェクトを取得してしまえば、そのオブジェクトを使って複数の操作(タイムスタンプ変更だけでなく、他のプロパティの変更など)を一連の流れで行えることです。

例6:[System.IO.FileInfo] オブジェクトを使って複数のタイムスタンプを同時に変更する

[System.IO.FileInfo]オブジェクトを使えば、同じオブジェクトに対して複数のタイムスタンププロパティに続けて値を代入できます。

“`powershell

例:C:\Temp\MyFile.txt の作成日時と最終更新日時を特定の日時に設定

$filePath = “C:\Temp\MyFile.txt”
$targetDateTime = [System.DateTime]::Parse(“2023-12-31 23:59:59”) # DateTimeオブジェクトとして作成

$file = Get-Item $filePath -ErrorAction SilentlyContinue

if (-not $file) {
Write-Error “ファイル ‘$filePath’ が見つかりません。”
exit
}

作成日時を設定

$file.CreationTime = $targetDateTime

最終更新日時を設定

$file.LastWriteTime = $targetDateTime

最終アクセス日時も設定 (注意点は前述の通り)

$file.LastAccessTime = Get-Date # 例として現在時刻に

Write-Host “$filePath の複数のタイムスタンプを設定しました (FileInfo)。”

確認

Get-Item $filePath | Select-Object Name, CreationTime, LastWriteTime, LastAccessTime
“`

このように、一つのオブジェクトに対して複数の変更を加える場合は、Set-ItemPropertyを複数回呼び出すよりも、[System.IO.FileInfo]オブジェクトのプロパティを直接操作する方がコードがシンプルになる場合があります。

[System.IO.File] クラスを使用する (静的メソッド)

[System.IO.File]クラスは、特定のファイルパスに対して直接操作を行うための静的なメソッドを提供します。オブジェクトを取得する必要がないため、単一のファイルに対して特定の操作だけを行いたい場合に便利です。

タイムスタンプ関連の静的メソッドは以下の通りです。

  • [System.IO.File]::SetCreationTime(path, dateTime)
  • [System.IO.File]::SetLastWriteTime(path, dateTime)
  • [System.IO.File]::SetLastAccessTime(path, dateTime)

これらのメソッドは、第一引数にファイルパス(文字列)を、第二引数に設定したい日付/時刻([System.DateTime]オブジェクト)を取ります。

例7:[System.IO.File] クラスの静的メソッドを使って最終更新日時を変更する

“`powershell

例:C:\Temp\MyFile.txt の最終更新日時を現在時刻に設定

$filePath = “C:\Temp\MyFile.txt”
$currentDateTime = Get-Date

ファイルが存在しない場合はエラーになるので注意

このメソッド自体にファイル作成機能はない

最終更新日時を設定

Write-Host “$filePath の最終更新日時を現在時刻に変更しました (File::SetLastWriteTime)。”

確認

Get-Item $filePath | Select-Object Name, LastWriteTime
“`

例8:[System.IO.File] クラスの静的メソッドを使って複数のタイムスタンプを変更する

複数のタイムスタンプを変更する場合も、それぞれのメソッドを呼び出します。

“`powershell

例:C:\Temp\MyFile.txt の作成日時と最終更新日時を特定の日時に設定

$filePath = “C:\Temp\MyFile.txt”
$targetDateTime = [System.DateTime]::Parse(“2023-12-31 23:59:59”)

ファイルが存在しない場合はエラーになるので注意

作成日時を設定

最終更新日時を設定

最終アクセス日時も設定 (注意点は前述の通り)

Write-Host “$filePath の複数のタイムスタンプを設定しました (File::Set*Time)。”

確認

Get-Item $filePath | Select-Object Name, CreationTime, LastWriteTime, LastAccessTime
“`

[System.IO.File]クラスの静的メソッドは、オブジェクトの取得を伴わないため、わずかに高速である可能性があります。しかし、通常はパフォーマンスの差は微々たるものです。どちらの方法を選ぶかは、スクリプトの全体的な構造や個人の好みに依存します。パイプライン処理と組み合わせる場合は、Get-ChildItemが返すオブジェクトに対して操作するSet-ItemProperty[System.IO.FileInfo]オブジェクトを直接操作する方法が自然です。単一ファイルに対してシンプルに操作したい場合は、[System.IO.File]::Set*Timeメソッドも選択肢になります。

Linux touch コマンドの機能とPowerShellでの実現方法の比較

ここで、Linuxのtouchコマンドの主要な機能と、それらをPowerShellでどのように実現できるかを比較してみましょう。

touchコマンドの機能 説明 PowerShellでの実現方法
touch ファイル名 ファイルが存在する場合:最終更新日時と最終アクセス日時を現在時刻に変更。
ファイルが存在しない場合:空の新しいファイルを作成し、タイムスタンプを現在時刻に設定。
ファイルが存在する場合:Set-ItemProperty ... -Name LastWriteTime -Value (Get-Date)Set-ItemProperty ... -Name LastAccessTime -Value (Get-Date) を実行するか、.NET FileInfo オブジェクトを操作。
ファイルが存在しない場合:New-Item -Path "ファイル名" -ItemType File または Out-File -Path "ファイル名" を実行(ファイル作成時にタイムスタンプが設定される)。
touch -a ファイル名 最終アクセス日時のみを現在時刻に変更。 Set-ItemProperty -Path "ファイル名" -Name LastAccessTime -Value (Get-Date) または .NET FileInfo/File を使用。
touch -m ファイル名 最終更新日時のみを現在時刻に変更。 Set-ItemProperty -Path "ファイル名" -Name LastWriteTime -Value (Get-Date) または .NET FileInfo/File を使用。
touch -t YYYYMMDDhhmm.ss ファイル名 指定した日時(UTC)に最終更新日時と最終アクセス日時を設定。 Set-ItemProperty ... -Name LastWriteTime -Value "YYYY-MM-DD HH:mm:ss"Set-ItemProperty ... -Name LastAccessTime -Value "YYYY-MM-DD HH:mm:ss" を実行するか、.NET FileInfo/File を使用。
(タイムゾーンの扱いに注意)。
touch -d "日時文字列" ファイル名 指定した日時文字列(より柔軟な形式)に最終更新日時と最終アクセス日時を設定。 Set-ItemProperty ... -Name LastWriteTime -Value "日時文字列"Set-ItemProperty ... -Name LastAccessTime -Value "日時文字列" を実行するか、.NET FileInfo/File を使用。
PowerShellの日付/時刻解析機能を利用。
touch -r 参照ファイル ターゲットファイル ターゲットファイルのタイムスタンプを参照ファイルのタイムスタンプと同じにする。 参照ファイルのタイムスタンプを取得し、その値をターゲットファイルに設定するスクリプトを記述。
$ref = Get-Item "参照ファイル"; Set-ItemProperty "ターゲットファイル" -Name LastWriteTime -Value $ref.LastWriteTime など。

ご覧の通り、touchコマンドの主要な機能は、PowerShellの既存のコマンドレットや.NETクラスを組み合わせることで十分に実現可能です。特に、Set-ItemProperty.NET FileInfo/Fileを使えば、最終更新日時と最終アクセス日時だけでなく、作成日時も操作できるため、より細かい制御が可能です。

touchコマンドの「ファイルが存在しない場合に作成する」という機能は、PowerShellではTest-Pathで存在を確認し、必要に応じてNew-ItemOut-Fileで作成するというステップになります。これを組み合わせることで、touchの完全な代替として機能するPowerShellスクリプトや関数を作成できます。

高度な使い方とスクリプト例:PowerShellでtouch風関数を作成する

Linux touchのように、ファイルパスを指定するだけでタイムスタンプを更新し、ファイルが存在しない場合は作成するような、より使いやすいコマンドレット風の関数をPowerShellで作成してみましょう。

この関数は以下の機能を持ちます。

  • ファイルパスを指定して、そのファイルのタイムスタンプを操作する。
  • デフォルトでは、Linux touchと同様に最終更新日時と最終アクセス日時を現在時刻に設定する。
  • ファイルが存在しない場合は新しく作成する。
  • 特定の日時を指定してタイムスタンプを設定できるようにする。
  • 別のファイルを参照して、そのタイムスタンプをコピーできるようにする (-ReferenceFile パラメータ)。
  • どのタイムスタンプ(作成日時、最終更新日時、最終アクセス日時)を変更するかを細かく制御できるようにする。

“`powershell
<#
.SYNOPSIS
Linuxの’touch’コマンドに似た機能を提供します。
ファイルのタイムスタンプ(最終更新日時、最終アクセス日時、作成日時)を変更し、ファイルが存在しない場合は作成します。

.DESCRIPTION
この関数は、指定されたファイルのタイムスタンプを操作します。
デフォルトでは、ファイルの最終更新日時と最終アクセス日時を現在時刻に設定します。
‘-Date’パラメータで特定の日時を指定したり、’-ReferenceFile’パラメータで別のファイルのタイムスタンプをコピーしたりできます。
‘-CreationTime’, ‘-LastWriteTime’, ‘-LastAccessTime’ スイッチを使用して、変更するタイムスタンプを個別に指定できます。
ファイルが存在しない場合、空のファイルを作成します。

.PARAMETER Path
操作対象となるファイルのパスを指定します。ワイルドカードも使用可能です。

.PARAMETER Date
ファイルのタイムスタンプに設定する特定の日時を指定します。
指定しない場合、現在時刻が使用されます。

.PARAMETER ReferenceFile
タイムスタンプをコピーする参照ファイルのパスを指定します。
このパラメータを指定した場合、’-Date’ パラメータは無視されます。
参照ファイルのタイムスタンプが操作対象ファイルにコピーされます。

.PARAMETER CreationTime
このスイッチを指定すると、ファイルの作成日時を変更します。
‘-Date’ または ‘-ReferenceFile’ で指定された日時が使用されます。

.PARAMETER LastWriteTime
このスイッチを指定すると、ファイルの最終更新日時を変更します。
‘-Date’ または ‘-ReferenceFile’ で指定された日時が使用されます。
デフォルトで有効です。

.PARAMETER LastAccessTime
このスイッチを指定すると、ファイルの最終アクセス日時を変更します。
‘-Date’ または ‘-ReferenceFile’ で指定された日時が使用されます。
デフォルトで有効ですが、Windows NTFSのデフォルト設定によってはアクセス時に自動更新されないことに注意してください。

.PARAMETER Force
読み取り専用ファイルなど、通常は変更が許可されないファイルに対しても強制的に操作を実行します。
現在、この関数では読み取り専用属性の解除のみを考慮しています。

.EXAMPLE
Touch-File “C:\Temp\MyFile.txt”

C:\Temp\MyFile.txt の最終更新日時と最終アクセス日時を現在時刻に設定します。

ファイルが存在しない場合は作成します。

.EXAMPLE
Touch-File “C:\Temp\LogFiles*.log” -Date “2023-10-26 14:00”

C:\Temp\LogFiles ディレクトリ内の全ての .log ファイルの最終更新日時と最終アクセス日時を

2023年10月26日 14:00 に設定します。

.EXAMPLE
Touch-File “C:\Temp\Report.docx” -ReferenceFile “C:\Templates\Standard.dotx”

C:\Temp\Report.docx の最終更新日時と最終アクセス日時を

C:\Templates\Standard.dotx の最終更新日時と同じにします。

.EXAMPLE
Touch-File “C:\Temp\Archive.zip” -CreationTime -LastWriteTime -Date “2024-01-01”

C:\Temp\Archive.zip の作成日時と最終更新日時を 2024年1月1日 00:00 に設定します。

最終アクセス日時は変更しません。

.EXAMPLE
Touch-File “C:\Temp\Important.txt” -LastWriteTime:$false -LastAccessTime:$false -CreationTime -Date (Get-Date).AddDays(-7)

C:\Temp\Important.txt の作成日時を7日前の日時に設定します。

最終更新日時と最終アクセス日時は変更しません。

.NOTES
LastAccessTimeはWindows NTFSのデフォルト設定により、アクセス時に自動更新されない場合があります。
この関数による設定は可能ですが、その後のOSによる更新は保証されません。
実行には、対象ファイルへの書き込み権限が必要です。

>

function Touch-File {
[CmdletBinding(SupportsShouldProcess=$true, DefaultParameterSetName=’SetDate’)]
param(
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[string[]]$Path,

    [Parameter(ParameterSetName='SetDate')]
    [datetime]$Date,

    [Parameter(ParameterSetName='SetReference')]
    [string]$ReferenceFile,

    [Parameter()]
    [switch]$CreationTime,

    [Parameter()]
    [switch]$LastWriteTime = $true, # デフォルトで有効

    [Parameter()]
    [switch]$LastAccessTime = $true,  # デフォルトで有効

    [Parameter()]
    [switch]$Force # 現時点では読み取り専用属性解除のみ考慮
)

BEGIN {
    # 設定する日時を決定
    $targetDateTime = $null

    switch ($PsCmdlet.ParameterSetName) {
        'SetDate' {
            # '-Date' が指定されていればその値、なければ現在時刻
            $targetDateTime = if ($PSBoundParameters.ContainsKey('Date')) { $Date } else { Get-Date }
            Write-Verbose "ターゲット日時 (SetDate): $($targetDateTime)"
        }
        'SetReference' {
            # 参照ファイルのタイムスタンプを取得
            try {
                $refFileInfo = Get-Item $ReferenceFile -ErrorAction Stop
                # デフォルトでは参照ファイルの最終更新日時を使用する
                $targetDateTime = $refFileInfo.LastWriteTime
                Write-Verbose "ターゲット日時 (SetReference - LastWriteTime of $ReferenceFile): $($targetDateTime)"

                # TODO: touch -r はアクセス日時もコピーする。
                # 必要であれば、ここで参照ファイルの LastAccessTime も取得しておき、
                # 各ファイルの LastAccessTime を設定する際に使用するようにロジックを追加。
                # $refLastAccessTime = $refFileInfo.LastAccessTime
                # Write-Verbose "参照ファイルの最終アクセス日時: $($refLastAccessTime)"

            } catch {
                Write-Error "参照ファイル '$ReferenceFile' が見つからないか、アクセスできません。"
                return # BEGINブロックなので、関数全体の処理を停止
            }
        }
    }

    # どのタイムスタンプを変更するかフラグを整理
    # スイッチの指定状況に応じて、変更対象のタイムスタンプのリストを作成する
    $timesToChange = @()
    if ($CreationTime) { $timesToChange += 'CreationTime' }
    if ($LastWriteTime) { $timesToChange += 'LastWriteTime' }
    if ($LastAccessTime) { $timesToChange += 'LastAccessTime' }

    # どのタイムスタンプも変更対象として指定されていない場合は、デフォルト(LastWriteTimeとLastAccessTime)を設定
    if ($timesToChange.Count -eq 0) {
         Write-Verbose "変更対象タイムスタンプが指定されていないため、LastWriteTimeとLastAccessTimeをデフォルトで有効化します。"
        $timesToChange = @('LastWriteTime', 'LastAccessTime')
    }

    Write-Verbose "変更対象タイムスタンプ: $($timesToChange -join ', ')"

} # END BEGIN

PROCESS {
    foreach ($itemPath in $Path) {
        Write-Verbose "処理対象ファイル: $($itemPath)"

        # ShouldProcess の確認 (PowerShellの標準機能)
        if ($PSCmdlet.ShouldProcess($itemPath, "ファイルのタイムスタンプ変更")) {

            $fileInfo = Get-Item $itemPath -ErrorAction SilentlyContinue -Force:$Force

            # ファイルが存在しない場合、作成する (touchの機能)
            if (-not $fileInfo) {
                Write-Verbose "ファイルが存在しません。作成します: $($itemPath)"
                try {
                    # New-Item で空のファイルを作成
                    $fileInfo = New-Item -Path $itemPath -ItemType File -Force:$Force -ErrorAction Stop
                    Write-Host "ファイル '$itemPath' を作成しました。"

                    # 新規作成されたファイルのタイムスタンプは、New-Item実行時の現在時刻になっているはずだが、
                    # touchの挙動に倣い、改めて指定された(または現在時刻の)$targetDateTimeに設定し直す
                    # 作成日時もここで設定する必要があるため、CreationTimeが$timesToChangeに含まれていなくても一度設定を試みる
                    # ただし、後続のSet-ItemPropertyで指定されたフラグに基づいて再度設定されるので、ここではLastWrite/Accessだけでも良いかもしれない
                    # 一旦、指定されたタイムスタンプのみを設定するロジックを適用する

                    # 新規作成されたファイルオブジェクトに対してタイムスタンプを設定
                    # 作成日時もデフォルトで変更対象とするか、LastWrite/Accessのみとするかは設計判断だが、
                    # touchは作成日時を変更しないので、ここではLastWriteとLastAccessを優先的に設定する
                    # ただし、パラメータでCreationTimeが明示的に指定されていればそれも設定

                    $newFileTimesToChange = @()
                    # 新規作成時は、CreationTimeも変更対象リストに含まれていれば変更
                    if ($timesToChange -contains 'CreationTime') { $newFileTimesToChange += 'CreationTime' }
                    # 新規作成時は、デフォルトでLastWriteTimeとLastAccessTimeは変更対象
                    if ($timesToChange -contains 'LastWriteTime' -or -not $PSBoundParameters.ContainsKey('LastWriteTime')) { $newFileTimesToChange += 'LastWriteTime' }
                     if ($timesToChange -contains 'LastAccessTime' -or -not $PSBoundParameters.ContainsKey('LastAccessTime')) { $newFileTimesToChange += 'LastAccessTime' }
                     # もし何も指定されていなかったらデフォルトでLastWrite/Access
                     if ($newFileTimesToChange.Count -eq 0) { $newFileTimesToChange = @('LastWriteTime', 'LastAccessTime') }

                    Write-Verbose "新規ファイルに対する変更対象タイムスタンプ: $($newFileTimesToChange -join ', ')"

                    # 新規作成されたファイルオブジェクトに対して、決定したターゲット日時を設定
                    # Set-ItemProperty はパイプラインでオブジェクトを受け付けられる
                    $fileInfo | ForEach-Object {
                        foreach ($timeName in $newFileTimesToChange) {
                            Write-Verbose "新規ファイル '$itemPath' の $timeName を設定: $($targetDateTime)"
                            $_."$timeName" = $targetDateTime # FileInfoオブジェクトのプロパティに直接代入
                        }
                    }

                    # 再度ファイル情報を取得して更新されたか確認しても良いが、今回は省略

                } catch {
                    Write-Error "ファイルの作成または初期タイムスタンプ設定に失敗しました: $($_.Exception.Message)"
                    continue # 次のファイルへ
                }

            } else {
                # ファイルが存在する場合
                Write-Verbose "ファイルが存在します: $($itemPath)"

                # Force パラメータが指定されている場合、読み取り専用属性を一時的に解除
                $originalIsReadOnly = $false
                if ($Force -and $fileInfo.IsReadOnly) {
                     Write-Verbose "$itemPath は読み取り専用です。一時的に解除します。"
                     $originalIsReadOnly = $true
                     try {
                         $fileInfo.IsReadOnly = $false
                         # Set-ItemProperty -Path $itemPath -Name IsReadOnly -Value $false -ErrorAction Stop # こちらでも可
                         Write-Verbose "$itemPath の読み取り専用属性を解除しました。"
                     } catch {
                         Write-Error "読み取り専用属性の解除に失敗しました: $($_.Exception.Message)"
                         continue # 次のファイルへ
                     }
                }


                # FileInfo オブジェクトに対してタイムスタンプを設定
                # Set-ItemProperty ではなく、FileInfoオブジェクトを直接操作することで、
                # パイプライン処理との親和性を保ちつつ、複数のプロパティ変更を連続で行える
                # または、foreach ($timeName in $timesToChange) { Set-ItemProperty -Path $itemPath -Name $timeName -Value $targetDateTime }
                # とする手もあるが、ここではオブジェクト操作を採用

                try {
                    foreach ($timeName in $timesToChange) {
                        Write-Verbose "$itemPath の $timeName を設定: $($targetDateTime)"
                        $fileInfo."$timeName" = $targetDateTime # FileInfoオブジェクトのプロパティに直接代入
                        # Set-ItemProperty -Path $itemPath -Name $timeName -Value $targetDateTime -ErrorAction Stop # こちらでも可
                    }
                } catch {
                    Write-Error "ファイルのタイムスタンプ設定に失敗しました ('$itemPath'): $($_.Exception.Message)"
                } finally {
                    # Force パラメータが指定されていて、元々読み取り専用だった場合は属性を戻す
                    if ($Force -and $originalIsReadOnly) {
                        try {
                            $fileInfo.IsReadOnly = $true
                            # Set-ItemProperty -Path $itemPath -Name IsReadOnly -Value $true -ErrorAction Stop # こちらでも可
                            Write-Verbose "$itemPath の読み取り専用属性を元に戻しました。"
                        } catch {
                            # 属性を戻せなくてもファイルのタイムスタンプは変更されている可能性が高いので、エラーとして報告するだけにする
                            Write-Warning "ファイル '$itemPath' の読み取り専用属性を元に戻せませんでした: $($_.Exception.Message)"
                        }
                    }
                }
            }
        }
    }
} # END PROCESS

# END ブロックは今回は特に処理なし

} # END function Touch-File

関数の使い方例:

例1:単一ファイルのタイムスタンプを現在時刻に更新(存在しない場合は作成)

Touch-File “C:\Temp\TestFile1.txt”

例2:複数のファイルのタイムスタンプを現在時刻に更新

Touch-File “C:\Temp*.log”

例3:特定の日時にタイムスタンプを設定

Touch-File “C:\Temp\TestFile2.txt” -Date “2023-11-15 10:30:00”

例4:参照ファイルのタイムスタンプをコピー

Touch-File “C:\Temp\TestFile3.txt” -ReferenceFile “C:\Windows\explorer.exe”

例5:作成日時のみを過去に設定

Touch-File “C:\Temp\TestFile4.txt” -CreationTime -LastWriteTime:$false -LastAccessTime:$false -Date (Get-Date).AddHours(-5)

例6:読み取り専用ファイルのタイムスタンプを強制的に変更

Touch-File “C:\Temp\ReadOnlyFile.txt” -Force

関数をPowerShellセッションで利用可能にするには、このスクリプトをドットソーシング (. .\path\to\script.ps1) するか、

プロファイルに登録してください。

“`

関数コードの解説:

  1. [CmdletBinding()]param(): 標準的なPowerShell関数の定義です。SupportsShouldProcess-WhatIf-Confirmパラメータをサポートし、操作の実行前に確認やテストができるようにします。DefaultParameterSetNameは、パラメータセットを指定せずに呼び出された場合のデフォルトの挙動を定義します。
  2. パラメータの定義:
    • Path: 操作対象のファイルパスを受け取ります。ワイルドカードやパイプライン入力(ValueFromPipeline=$true)をサポートします。
    • Date: 設定する特定の日時を指定します。SetDateパラメータセットの一部です。
    • ReferenceFile: タイムスタンプをコピーする参照ファイルを指定します。SetReferenceパラメータセットの一部です。DateReferenceFileは同時に指定できません。
    • CreationTime, LastWriteTime, LastAccessTime: どのタイムスタンプを変更するかを制御するスイッチパラメータです。LastWriteTimeLastAccessTimeはデフォルトで$true(有効)になっています。:$falseを付けて無効にすることもできます(例: -LastWriteTime:$false)。
    • Force: 読み取り専用などの属性を一時的に解除して操作を強制します。
  3. BEGIN ブロック: パイプラインからの入力処理が始まる前に一度だけ実行されます。
    • パラメータセット(SetDateSetReferenceか)に応じて、$targetDateTime変数に設定すべき日時を決定します。
    • SetReferenceセットの場合は、Get-Itemで参照ファイルの情報を取得し、デフォルトではそのLastWriteTimeをコピー対象の日時とします。(Linux touch -r は通常、更新日時とアクセス日時の両方をコピーしますが、この関数ではターゲット日時として単一の[System.DateTime]オブジェクトを主に扱うため、デフォルトではLastWriteTimeを基本とし、必要に応じて拡張してください。)
    • -CreationTime, -LastWriteTime, -LastAccessTime スイッチの指定状況を見て、実際に変更対象とするタイムスタンプ名のリスト$timesToChangeを作成します。何も指定されていない場合は、デフォルトでLastWriteTimeLastAccessTimeを対象とします。
  4. PROCESS ブロック: パイプラインから渡された各入力オブジェクト(ここでは$Pathで指定されたファイルパス)に対して実行されます。
    • $PSCmdlet.ShouldProcess()を呼び出すことで、-WhatIf-Confirmのロジックを組み込みます。
    • Get-Itemで現在のファイル情報を取得します。-ErrorAction SilentlyContinue-Force:$Forceを使用しています。
    • ファイルが存在しない場合: New-Item -ItemType Fileで空のファイルを新しく作成します。作成されたファイルオブジェクトは$fileInfoに格納されます。その後、$timesToChangeリストに基づいて、作成されたファイルのタイムスタンプを$targetDateTimeに設定し直します。新規作成時には、デフォルトでLastWriteTimeLastAccessTimeが設定されるようにしています。
    • ファイルが存在する場合:
      • -Forceが指定されていてファイルが読み取り専用の場合、一時的に読み取り専用属性を解除します。
      • $fileInfoオブジェクトのプロパティ(CreationTime, LastWriteTime, LastAccessTime)に対して、$timesToChangeリストに含まれるものだけ、$targetDateTimeの値を代入してタイムスタンプを変更します。.NET FileInfoオブジェクトのプロパティへの直接代入を利用しています。
      • 読み取り専用属性を一時的に解除した場合は、最後に属性を元に戻します。
    • エラー処理としてtry-catchブロックを使用し、ファイル作成失敗やタイムスタンプ設定失敗などのエラーを捕捉しています。

このTouch-File関数を使えば、Linuxのtouchコマンドに近い感覚でPowerShellからファイルのタイムスタンプを操作できます。必要に応じて、機能を追加したり、エラー処理を強化したり、より洗練されたパラメータの組み合わせに対応させたりすることも可能です。

注意点とトラブルシューティング

ファイルのタイムスタンプ操作を行う際には、いくつか注意すべき点があります。

  1. アクセス権限: 対象のファイルやその親フォルダに対して、書き込み(変更)の権限が必要です。権限がない場合はエラーが発生します。必要であれば、PowerShellを管理者として実行したり、Get-Acl / Set-Aclコマンドレットを使って権限を変更したりする必要があります。
  2. LastAccessTimeの挙動: 前述の通り、Windows NTFSではデフォルトでLastAccessTimeの自動更新が無効または遅延されています。手動でSet-ItemProperty.NETクラスを使って設定した値は反映されますが、その後のファイルアクセスによって期待通りに自動更新されない可能性が高いです。LastAccessTimeは、他のタイムスタンプに比べて信頼性が低い情報であることを念頭に置いて使用してください。
  3. ファイルシステムの制限: 異なるファイルシステム(例: FAT32 vs. NTFS)間でのファイルのコピーや移動、あるいは古いファイルシステムでは、タイムスタンプの粒度(秒以下の精度など)や扱いに違いがある場合があります。
  4. 日時文字列の解析: -Valueパラメータや.NETメソッドに日時を文字列として渡す場合、PowerShellや.NETがその文字列を正しく[System.DateTime]オブジェクトとして解析できる形式である必要があります。ISO 8601形式 (YYYY-MM-DD HH:mm:ss) など、明確な形式を使用するのが最も安全です。[System.DateTime]::Parse()[System.DateTime]::ParseExact()メソッドを使って、明示的に文字列を解析することもできます。
  5. DST(夏時間): 日時を指定する際に、夏時間が有効な期間かどうかによって時刻がずれる可能性があります。特にタイムゾーンや夏時間を意識した厳密な時刻設定が必要な場合は、[System.DateTimeOffset]クラスを使用したり、UTC時刻 ([System.DateTime]::UtcNow) を基準に考えたりするなど、より注意深い扱いが必要になる場合があります。しかし、ほとんどの一般的なユースケースでは、ローカル時刻 (Get-Date) で十分です。

まとめ

この記事では、PowerShellでファイルのタイムスタンプ(作成日時、最終更新日時、最終アクセス日時)を変更する様々な方法について詳細に解説しました。

  • 最も基本的な方法として、Set-ItemPropertyコマンドレットを使用して、ファイルオブジェクトのCreationTime, LastWriteTime, LastAccessTime プロパティの値を変更する方法を紹介しました。
  • PowerShellが利用できる.NET Framework/.NET Coreのクラス([System.IO.FileInfo]および[System.IO.File])を直接使用してタイムスタンプを操作する方法も紹介しました。[System.IO.FileInfo]オブジェクトのプロパティを直接操作する方法は、複数のタイムスタンプを効率的に変更したい場合に便利です。[System.IO.File]クラスの静的メソッドは、単一ファイルに対するシンプルな操作に適しています。
  • Linuxのtouchコマンドの主要な機能(タイムスタンプの現在時刻への更新、特定日時への設定、参照ファイルからのコピー、ファイルが存在しない場合の作成)が、これらのPowerShellの機能の組み合わせで十分に代替可能であることを示しました。
  • これらの機能を組み合わせ、Linux touchコマンドの使い勝手に近いTouch-Fileという名前のカスタム関数を作成する例を示しました。この関数は、ファイルが存在しない場合の作成機能や、特定日時・参照ファイルからのタイムスタンプ設定、変更対象タイムスタンプの制御などの機能を含んでいます。
  • タイムスタンプ操作に関する注意点、特にWindows NTFSにおけるLastAccessTimeの挙動やアクセス権限などについても触れました。

PowerShellは非常に柔軟で強力なシェル環境であり、ファイルシステムの低レベルな操作も効率的に行うことができます。この記事で紹介した方法を活用することで、ファイルのタイムスタンプを自由に制御し、様々なシナリオに対応できるようになるでしょう。目的やスクリプトの複雑さに応じて、Set-ItemProperty.NET FileInfo、または.NET Fileのいずれかの方法、あるいはそれらを組み合わせたカスタム関数を選択してください。

これで、PowerShell環境でLinuxのtouchコマンドが行うような、あるいはそれ以上のファイルのタイムスタンプ操作を自信を持って実行できるはずです。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール