Windowsでgit bash(mingw64/cygwin)を使って画像、動画ファイルの更新日時、作成日時をexifを元に変更する(ChatGPT習作)

tl;dir

SEARCH_DIR="/path/to/directory"

find "$SEARCH_DIR" -type f \( -iname "*.jpg" -o -iname "*.JPG" -o -iname "*.jpeg" -o -iname "*.JPEG" -o -iname "*.png" -o -iname "*.PNG" -o -iname "*.heic" -o -iname "*.HEIC" -o -iname "*.mov" -o -iname "*.MOV" -o -iname "*.mp4" -o -iname "*.MP4" \) -exec bash -c '
  for unix_file; do
    datetime=$(exiftool -d "%Y-%m-%dT%H:%M:%S" -DateTimeOriginal "$unix_file" | awk -F": " '\''{print $2}'\'')
    if [ ! -z "$datetime" ]; then
      echo "unix_file:$unix_file"
      touch -am -d "$datetime" "$unix_file"
      win_file=$(echo "$unix_file" | sed "s/^\/\([a-z]\)\//\1:\//; s/\//\\\\/g")
      echo "win_file:$win_file"
      powershell.exe -Command "\$file = Get-Item \"$win_file\"; \$file.CreationTime = [datetime]::ParseExact(\"$datetime\", \"yyyy-MM-ddTHH:mm:ss\", \$null); \$file.LastWriteTime = [datetime]::ParseExact(\"$datetime\", \"yyyy-MM-ddTHH:mm:ss\", \$null);"
    fi
  done
' bash {} +

目的

  • Google Photosの画像、動画ファイルを全部Dropboxへ移行したい
  • 移行の際には撮影日を元にフォルダー分けしたい
  • そのためにファイルの作成日時、更新日時を撮影日時に変更したい
  • 撮影日時はexif情報が使える想定

環境を用意する

  • WSL2のセットアップする
  • git for WindowsInstalling ExifTool
    • git bashをインストールするつもりでしたが今はgit for Windowsというものがある(統合?移行?)ようなのでこちらをインストールしました
    • Unix Toolsもインストールしています。gitが欲しい訳でなくLinux的なbashなシェル環境が欲しいので
  • exiftoolInstalling ExifToolをインストールする
    • これが画像や動画からexif情報を抜き出すコマンドです。元はPerlスクリプトですがWindows用のexeで配布してくれていました。感謝
    • サイトからDLしたらC:\Windows に移動します。パスを通すためです

exifの情報を元にファイルの更新日を変更する

exiftを取得するには何がいいんだろとググるとexiftoolがすぐに出てきました。 これはUnixコマンドで中身はPerlのようです。

作業環境はWindowsなのでできれば何かのソフトでボタンポチポチでできたらいいなと思い更にググるとよさそうなアプリがいくつかありました。

特にこちらのF6 Exifは素晴らしく、jpegファイルなら深い階層まで確実にファイルの更新日時を変更してくれます。感謝。 F6 ExifF6 Exifの詳細情報 : Vector ソフトを探す!

ただ、最終更新日が2007.08.15の通り、HEICどころかpngにも対応していません。 ソフトの範囲外の要求なので仕方ないですね。

それに、このソフトが丁寧に更新日時を更新してくれたので作成日時も更新したいという欲が出てきました。

という訳で作成日時をgit bash&exiftoolを使って作成日時を更新するスクリプトを作成することにしました。

ChatGPTにコードを作成してもらう

git bashで頑張る編

要求と回答は以下です。

河童:

画像ファイルに含まれるexif情報を元にファイルの更新日を変更したいです。
現在の状況は以下です。
- 環境はLinuxのbash
- ディレクトリーの下の複数階層にファイルが存在する
- 画像の形式はjpgだけでなく、pngやheicやmovなど様々

また、この問題を解くために必要な情報があれば私に質問してください。

ChatGPT:

#!/bin/bash

# 検索するディレクトリのパス
SEARCH_DIR="/path/to/directory"

# findコマンドでディレクトリ内のすべてのファイルを検索
find "$SEARCH_DIR" -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.heic" -o -iname "*.mov" \) -exec bash -c '
  for file; do
    # exiftoolを使用して撮影日時を取得
    datetime=$(exiftool -DateTimeOriginal "$file" | awk -F": " '\''{print $2}'\'')
    if [ ! -z "$datetime" ]; then
      # 撮影日時をファイルの更新日時に設定
      touch -d "$datetime" "$file"
    fi
  done
' bash {} +

画像、動画のexifを元にファイルの更新日を変更する
prompt1

中々良さそうに見えますね。 でもこのスクリプト$datetimeの値は年月日 時分秒と間に空白が入ってしまう形式でした。 touchコマンドの日時指定はISO8601形式です。YYYY-MM-ddTHH:mm:ssなやつですね。なので失敗します。

自分のISO8601の認識が間違っているのかな、RFC3339の方だっけ?と思ってこのサイトで確認しました。 私の認識通り正しいようです。

RFC 3339 vs ISO 8601

ただ、RFC3339でもT区切りは許容されているようですね。UTC明示が末尾ZなのはISO8601では許可されずRFC3339独自のようです。学びがありますね。

という訳でRFC8601形式かつ -%d %H の間に T を入れて作成し直してもらったスクリプトがこちらです。

#!/bin/bash

# 検索するディレクトリのパス
SEARCH_DIR="/path/to/directory"

# findコマンドでディレクトリ内のすべてのファイルを検索
find "$SEARCH_DIR" -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.heic" -o -iname "*.mov" \) -exec bash -c '
  for file; do
    # exiftoolを使用して撮影日時を取得し、ISO8601形式に変換(日付と時刻の間にTを挿入)
    datetime=$(exiftool -d "%Y-%m-%dT%H:%M:%S" -DateTimeOriginal "$file" | awk -F": " '\''{print $2}'\'')
    if [ ! -z "$datetime" ]; then
      # ISO8601形式の日時をtouchコマンドで利用する形式に変換
      # 注意: GNU版のtouchが必要です
      touch -d "$datetime" "$file"
    fi
  done
' bash {} +

これでファイル日時の更新ができるようになりました。良き。

PowerShellも使ってみる編

更新日時は変更できるようになりました。次の目標は作成日時も変更です。

色々試しましたがUnixという範囲内だと作成日時は変更できないようです。

そこでどうせWindows環境なのでPowerShellでの作成日時の変更方法を聞いてみました。

河童:

Windowsでgit bashを使ってファイルの作成日時を変更することはできますか?
もし可能ならbashスクリプトを作成してください。

ChatGPT:

WindowsでGit Bashを使用してファイルの作成日時を変更することは、直接的な方法では難しいかもしれません。これは、UNIX系システムのtouchコマンドがWindows上のGit Bashではファイルの作成日時(creation time)を変更する機能をサポートしていないためです。しかし、ファイルの変更日時(modification time)やアクセス日時(access time)は変更可能です。

Windowsでファイルの作成日時を変更するには、PowerShellを使う方法がありますが、Git BashからPowerShellのコマンドを呼び出すことで、目的を達成することが可能です。

以下は、Git Bashを使用してPowerShellコマンドを実行し、特定のファイルの作成日時を変更するためのbashスクリプトの例です。この例では、ファイルの作成日時を2024年1月1日午前0時0分に設定しています。

コード略

このスクリプトは、指定されたファイルの作成日時を新しい日時に変更します。FILE_PATH変数には、作成日時を変更したいファイルのパスを設定してください。

注意点
このスクリプトはGit Bash上で実行されることを想定していますが、実際にはPowerShellのコマンドを呼び出しています。そのため、Git BashとPowerShellの両方がWindowsシステム上で使用可能である必要があります。
ファイルパスがスペースを含む場合は、適切にクォートするか、エスケープ処理を行う必要があります。
この方法で、Git Bashを使用しつつもWindowsシステム上でファイルの作成日時を変更することが可能です。
#!/bin/bash

# 変更対象のファイルパス
FILE_PATH="/path/to/your/file.txt"

# PowerShellを使用してファイルの作成日時を変更
powershell.exe -Command "(Get-Item '$FILE_PATH').creationTime=$(Get-Date '2024-01-01 00:00:00')"

PoweShellで作成日時を変更する
prompt2

このスクリプトで作成日時を変更することができました。 ChatGPT、賢い。

ここまで来たら後は最初のスクリプトにこれをガッシャンコするだけ!

と思いましたがここからがとても時間がかかりました。

具体的には以下の点を解決する必要がありました

  • git bashとPowerSellではパスの記法が違うので変換する
  • git bashPowerShellで使う変数fileが被るので別々にする
  • PowerShell側のオプションでクォートが間違っているので正しくエスケープする

この辺りからはもうプロンプト指示だけだとまどろっこしいのでスクリプトを直接編集して、これを元に修正してと指示をして、また別の間違いがあったら具体例を記載して置き換えてもらって、のくりかえしです。

という訳でやっと目標の画像、動画ファイルのexif情報を元にファイルの更新日時、作成日時を変更するスクリプトが完成しました。

結局、movとmp4だけは作成日時の変更がうまくできていないのですがファイル全体からすると少ない件数なのでそちらはファイルの詳細情報(exifまたはXMP)を元に手動で対応します。