複数のファイルのタイムスタンプを 一括で変更するためのPowerShellスクリプト


概要

以下に理由を挙げた通り、IT技術者の倫理規範として、ファイルのタイムスタンプを故意に変更すべきではありません。

  • タイムスタンプの新旧関係の齟齬により、プログラムが誤動作するおそれがある。
  • タイムスタンプは、インシデント発生時における原因究明のための材料の一つとなる。
  • 捏造を疑われる。

環境

Windows10, Windows 7

使用方法

  • オススメの使用方法は、「送る」メニューに "ModTS.bat" をショートカットファイルとして登録しておき、タイムスタンプを変更したいファイルを右クリックして「送る」メニューから使用することです。

  • 通常の使用方法は、タイムスタンプを変更したいファイルを選択し、"ModTS.bat" にDrag&Dropします(ファイルまたはフォルダのパスが引数に指定されてバッチが起動します)。

  • ファイルへのD&Dが困難な場合は、バッチファイルを単体で起動するとフォルダパスの入力を求められるため、ファイルをD&Dして [Enter]キーを押下します。ファイルパス(複数ファイル指定不可)をペーストする方法もあります。

  1. 上記いずれかの方法で、変更対象のファイルを指定して起動します。
  2. 1ファイル目のタイムスタンプ(作成日時・更新日時)が表示されるため、その表記に倣い変更後のタイムスタンプを入力して [Enter]キーを押下します。
  3. エクスプローラにて、ファイルのタイムスタンプが変更されたことを確認します。

補足事項

  • 通常、バッチファイル起動後に対話形式で新しいタイムスタンプを入力しますが、第1引数に "-time"、第2引数にタイムスタンプを記述することで、新しいタイムスタンプを指定可能です。
  • 既定では、更新日時のみ変更されます。作成日時も変更したい場合は、第1引数に "-create" を指定します。
  • 新しいタイムスタンプを引数で指定し、作成日時も変更したい場合は、第1引数を "-timecreate" のようにし、第2引数にタイムスタンプを記述します。
  • 引数はスペース記号 で分割される仕様のため、ファイルパスやタイムスタンプにスペース記号が含まれる場合は、二重引用符 '"' で囲みます。タイムスタンプの日付と時刻の間は、"2019-07-15T10:08:42" のように、スペース記号の代わりに 'T' で繋ぐことも可能のようです。

ソースファイル

「マジック生成」するには、本ページ全体を選択してコピー後にB642FHT.batを起動して下さい。
その後、生成したZIPファイルを解凍して任意の場所へ配置して下さい。

コード

ModTS.bat
@ECHO OFF
PowerShell -ExecutionPolicy RemoteSigned -File %~dpn0.ps1 %* "nul" "nul"
EXIT /B
ModTS.ps1
$Host.UI.RawUI.ForeGroundColor = "Green"

"############## ModTS.ps1 ###############"
"# Modiify the timestamp of files batch #"
"#                                      #"
"#   1st release: 2019-07-15            #"
"#   Last update: 2019-07-25            #"
"#   See the web for more information   #"
"#   https://qiita.com/x-ia             #"
"########################################"

$nameFileScript = (Get-ChildItem $MyInvocation.MyCommand.Path).BaseName
$dirPathScript = Split-Path $MyInvocation.MyCommand.Path -Parent
$extLog = ".log"
$Host.UI.RawUI.WindowTitle = $nameFileScript
$pathFileLog = $dirPathScript + "\" + $nameFileScript + $extLog
$flagCont = 1
$flagCreate = 0
$iArg = 0
$numIn = 0
$numOut = 0
$timeNew = $null

if ($Args[0] -like "-*create*") {
  $flagCreate = 1
  $iArg = 1
}

if (($Args[0] -like "-*time*") -And ($Args[1] -match ".*[0-9].+[-/.][0-9].+[ T]+[0-2][0-9](:[0-5][0-9])+.*")) {
  $timeNew = $Args[1]
  $iArg = 2
}

if ($Args[$iArg] -ne "nul") {
  $flagCont = 0
}

do {
  $arg = $Args[$iArg]
  if ($arg -eq "nul") {
    # Input src dir and output file
    Write-Host "`r`nEnter the file path to modify the timestamp."
    Write-Host "To exit, hit the Enter key w/o any characters."
    $arg = Read-Host
    if (($arg -eq $null) -Or ($arg -eq "")) {
      Write-Host "`r`nTerminated by user.`r`n"
      break
    }
    if ((Test-Path $arg.Replace('"', '')) -ne $true) {
      Write-Host "`r`nFile not exists.`r`n"
      continue
    }
  } else {
    Write-Host "`r`nThe file to be modified:`r`n$arg`r`n"
  }

  $pathFileMod = $arg.Replace('"', '')
  ++$numIn

  while ($true) {
    if ((Test-Path $pathFileMod) -ne $true) {
      Write-Host "The files to be modified not exists.`r`n"
      break
    }

    $timeCreate = $(Get-ItemProperty $pathFileMod).CreationTime.ToString("yyyy-MM-dd HH:mm:ss.fff")
    $timeMod = $(Get-ItemProperty $pathFileMod).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss.fff")
    Write-Host "Created:  $timeCreate"
    Write-Host "Modified: $timeMod"

    if ($timeNew -eq $null) {
      Write-Host "`r`nEnter the new timestamp"
      $timeNew = Read-Host
    }

    $timeStampNow = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss.fff")

    if ($flagCreate -eq 1) {
      Set-ItemProperty $pathFileMod -Name CreationTime -Value "$timeNew"
    }
    Set-ItemProperty $pathFileMod -Name LastWriteTime -Value "$timeNew"
    Write-Host "Done modifying the timestamp.`r`n"
    ++$numOut
    break
  }

  "$timeStampNow`t$nameFileScript`t$pathFileMod`t$timeCreate`t$timeMod" | `
  Out-File $pathFileLog -Encoding Default -Append
  $Host.UI.RawUI.WindowTitle = "$nameFileScript $numOut/$numIn"

  if ($Args[$iArg] -ne "nul") {
    ++$iArg
  }
} while (($($Args[$iArg]) -ne "nul") -Or ($flagCont -eq 1))

バイナリ (Base64 encoding)

ModTS.zip
---
UEsDBBQAAAAIACGQ7k7pONSjWQAAAFwAAAAJAAAATW9kVFMuYmF0c3B19vBX8Hdz
4+UKyC9PLQrOSM3JUdB1rUhNLi3JzM8LyM/JTK5UCErNzS9JDc5Mz0tNUdB1y8xJ
VVCtSynIM9ArKDZUUNVSUMorzVGCkLxcrhGeIQr6TgBQSwMEFAAAAAgAUbX5ToNn
1NAaBAAALwsAAAkAAABNb2RUUy5wczGVVm1P4zgQ/o7EfxiZSrR0k7ZIq9NWug97
sAtIWxbR3u0HDok0mbYWiZ11nC3VHf/9xnYSnFJeLh/AHo+feXtm3AOYog6upqc4
L5cQzFQUI4z29zrnstDhnxfhdbSmv1+lwjMlS5GcyFQq+B3YmUIUbH9vf48dtD6Y
yGQ2DfNiBO2DA2Z0zTHniw3oFYLmGRY6ynKQC1jwFAuYRzpeQaX7rq/RHRUaFKYY
FTiG4+HoUzD8LRh93Kn7LSLlMk8i7eke79adIlpv1ziHBUWfUTaAC1pmkeZS+Lor
rfNiPBj85FxHYSyzwUPAIx+20n3nZzPcEVGGXyk901jxXFP6u2dUtpMVT5MLjRl0
JpsL8UvG1p1wsjmRWRaJJLyK9KoX/kEJuSQEAkq4MrIGZ5qnnOpPolcxgFQUCk0I
+KC/yaVhQJjKJXtGlR9cJHI94zpFUtrynLRzgjMCB7LlUB/Y34z+bgdMEmeXABZp
tDyRwng/qrcKqY4kGJKAf1bLainK7EI8rb+XutoY3l3i2jpYpqnJMV9At0NXi5vh
LQQpv0dgwVFskY9YD/7Z3wNoWxtZUWWPNo81zg4gY9HABJ9FUhsa0Xlm6c7Co5th
8Ok27N8Eg/C2XsPstk/rYyfojunfR7fu9UNCq73ywqmAfc+OPc/cuT0h44Jco/jb
0bnUDqtLiazOIovl3zdii2mOAvzZwiKaw4XIKeOFioGqDMQlkKU2ItPpTumH4hoD
wyBgd+pOfBEalW02owOGLKAldVzybGSE7DnETAI+cP0BVlxbbYd3jxtYDyS5sIF4
FdGQI2lRA1ShXWOUWBgndXWsQ7M0oep9V368TQV2hTJDlXFBTElgvoGyQBUaMav1
50Sje7d59EzOKLqqIclOeI15SjO5e8gOP8DhIRk0VetoVeIrtk3rgJDaZKPQRdtw
TBXmokTP9iNgWmCN9yySuhxUiTm6YnBMxubMONmgW8ZQQusep1FvOLMrDqPX77sO
dbfWK2Oi2w5tOyUe8tuZqP0uthx/MTPtklTsMIRrWr5jB6+ZuVdK5qj0pu1TaDVp
gM7oVjiTU624WHbZhr5gMgmSBM7Px1k2LopwsViwnmekytZbFszLZaP8fyb8xLhw
knEruh3tNKkr3TjI6rTYxq/njtchL1LyqbMF3Wi6uMm9N8S2OrFdiqm5dSnX9SN4
Sq733pcGz3NvjhvnR57j09fSD4F5SMGvMgR/RWlJk7SOgLWa+j1wrZK+hOcn9FSK
is4bCnprLnqUdh1G757bNvyuUspaGb3TW88uCTxfafdElmpjGAH/wp0BIyuBnTut
Jz74ImLyk5w8xUVUpppewDxHkdhB8dpPB7b9I6AKZeCGRsXENx81mwRzVMX9WA+a
bqd1seffdHO+eQ4dRYhB/wFQSwECFAAUAAAACAAhkO5O6TjUo1kAAABcAAAACQAA
AAAAAAABACAAAAAAAAAATW9kVFMuYmF0UEsBAhQAFAAAAAgAUbX5ToNn1NAaBAAA
LwsAAAkAAAAAAAAAAQAgAAAAgAAAAE1vZFRTLnBzMVBLBQYAAAAAAgACAG4AAADB
BAAAAAA=
---