カレントディレクトリにあるjpgファイルのうちEXIF情報に撮影日時があるファイルを、YYYYMMDDHHMMSS.jpgというファイル名に変更するPowerShellコマンドを作成してください。

ただし、撮影日時が未来の日付だったり1970年代だったりと不正な値の場合はファイル名を変更せずスキップしてください。 また、リネームしようとしたファイル名のファイルが既に存在した場合は、ファイル名の後に通し番号をつけてください。 ファイル名を変更した際は、Rename 旧ファイル名(ディレクトリパス含まず) -> 新ファイル名 というプロンプトを表示するようにしてください。

以下のPowerShellスクリプトは、指定された条件を満たす形で実装されています。このスクリプトを .ps1 ファイルとして保存して実行してください。

# 必要なモジュールのインポート
Add-Type -AssemblyName System.Drawing

# カレントディレクトリのjpgファイルを取得
Get-ChildItem -Path . -Filter *.jpg | ForEach-Object {
  $file = $_
  $fullPath = $file.FullName
  $fileName = $file.Name

  try {
    # EXIF情報の取得
    $image = [System.Drawing.Image]::FromFile($fullPath)
    $prop = $image.PropertyItems | Where-Object { $_.Id -eq 0x9003 } # 撮影日時プロパティ
    $image.Dispose()

    if ($prop -ne $null) {
      # 撮影日時の抽出
      $dateTaken = [System.Text.Encoding]::ASCII.GetString($prop.Value).TrimEnd([char]0)
      $parsedDate = [datetime]::ParseExact($dateTaken, "yyyy:MM:dd HH:mm:ss", $null)

      # 日付の妥当性チェック (未来の日時または1970年以前を無効とする)
      if ($parsedDate -gt (Get-Date) -or $parsedDate.Year -lt 1970) {
        Write-Output "Skip $fileName: Invalid date ($dateTaken)"
        return
      }

      # 新しいファイル名の生成
      $newFileName = $parsedDate.ToString("yyyyMMddHHmmss") + ".jpg"
      $newFilePath = Join-Path -Path (Get-Location) -ChildPath $newFileName
      $counter = 1

      # 重複するファイル名がある場合は通し番号を付加
      while (Test-Path $newFilePath) {
        $newFileName = $parsedDate.ToString("yyyyMMddHHmmss") + "_$counter.jpg"
        $newFilePath = Join-Path -Path (Get-Location) -ChildPath $newFileName
        $counter++
      }

      # ファイルのリネーム
      Rename-Item -Path $fullPath -NewName $newFileName
      Write-Output "Rename $fileName -> $newFileName"
    } else {
      Write-Output "Skip $fileName: No EXIF date information"
    }
  } catch {
    Write-Output "Error processing $fileName: $_"
  }
}

スクリプトの説明

  1. カレントディレクトリ内のJPEGファイルを取得

    Get-ChildItem を使用して、カレントディレクトリ内の .jpg ファイルをリストアップします。

  2. EXIF情報の撮影日時プロパティを取得

    撮影日時プロパティ(ID: 0x9003)を取得します。

  3. 日付の妥当性を確認

    撮影日時が未来または1970年以前の場合はスキップします。

  4. 新しいファイル名を生成

    撮影日時を元に YYYYMMDDHHMMSS.jpg 形式の新しいファイル名を生成し、重複があれば通し番号を付加します。

  5. リネームの実行とログの出力

    ファイルをリネームし、変更内容を Rename 旧ファイル名 -> 新ファイル名 形式で出力します。

注意事項

$fileName: が識別子として認識されます。${fileName}としてください。

以下の修正版では、変数を適切に ${} 形式で囲みました。これにより、スクリプトが期待どおりに動作するはずです。

# 必要なモジュールのインポート
Add-Type -AssemblyName System.Drawing

# カレントディレクトリのjpgファイルを取得
Get-ChildItem -Path . -Filter *.jpg | ForEach-Object {
  $file = $_
  $fullPath = $file.FullName
  $fileName = $file.Name

  try {
    # EXIF情報の取得
    $image = [System.Drawing.Image]::FromFile($fullPath)
    $prop = $image.PropertyItems | Where-Object { $_.Id -eq 0x9003 } # 撮影日時プロパティ
    $image.Dispose()

    if ($prop -ne $null) {
      # 撮影日時の抽出
      $dateTaken = [System.Text.Encoding]::ASCII.GetString($prop.Value).TrimEnd([char]0)
      $parsedDate = [datetime]::ParseExact($dateTaken, "yyyy:MM:dd HH:mm:ss", $null)

      # 日付の妥当性チェック (未来の日時または1970年以前を無効とする)
      if ($parsedDate -gt (Get-Date) -or $parsedDate.Year -lt 1970) {
        Write-Output "Skip ${fileName}: Invalid date (${dateTaken})"
        return
      }

      # 新しいファイル名の生成
      $newFileName = $parsedDate.ToString("yyyyMMddHHmmss") + ".jpg"
      $newFilePath = Join-Path -Path (Get-Location) -ChildPath $newFileName
      $counter = 1

      # 重複するファイル名がある場合は通し番号を付加
      while (Test-Path $newFilePath) {
        $newFileName = $parsedDate.ToString("yyyyMMddHHmmss") + "_$counter.jpg"
        $newFilePath = Join-Path -Path (Get-Location) -ChildPath $newFileName
        $counter++
      }

      # ファイルのリネーム
      Rename-Item -Path $fullPath -NewName $newFileName
      Write-Output "Rename ${fileName} -> ${newFileName}"
    } else {
      Write-Output "Skip ${fileName}: No EXIF date information"
    }
  } catch {
    Write-Output "Error processing ${fileName}: $_"
  }
}

主な変更点

このスクリプトを実行して動作確認してください。

まれに「Rename-Item -Path $fullPath -NewName $newFileName」の行で「The process cannot access the file because it is being used by another process.」というエラーが発生します。

このエラーが発生した場合200ミリ秒ごとに5回まで処理をリトライしてください。5回とも失敗した場合はエラーとみなして処理を停止してください。