シェルで画像ファイルの作成日時を撮影日時に変える(Mac)


スマホなどで撮影した画像をPCに取り込んだとき、取り込み日時がファイルの作成日時として設定されてしまい、撮影日時順で並べ替えができなくて不便だったりするので。

$ find . -maxdepth 1 -name '*.jpeg' -print0 | xargs -0 -I @ sh -c "sips -g creation '@' | sed -e '1d' |  awk '{print \$2,\$3}' | sed -E 's/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/\\1\\2\\3\\4\\5.\\6/g' | xargs -I TS touch -t TS '@'"

これでカレントディレクトリの *.jpeg ファイルすべての作成日時を撮影日時に変更することができる。もっと短く書ける方法が知りたい・・・。

以下、解説。

撮影日時の取得

まずはすべてのJPGファイルの撮影日時を取得してみようと思った。

$ for file in `find . -maxdepth 1 -name '*.jpeg'`; do
  sips -g creation "$file" | sed -e '1d'
done

これで概ねOKなのだけど、ファイル名にスペースが含まれていると、forに渡されるときにファイル名が分割されてしまう。たとえば、 IMG_1234 (1).jpeg というファイルがあると、次のように別々に処理されることになる。そして警告がでる。

sips -g creation "IMG_1234" | sed -e '1d'
sips -g creation "(1).jpeg" | sed -e '1d'

すこし調べたところ、forをつかっている限りこれは解消できなそうだったので、xargsを使った方法を模索する。 findの出力をスペースではなくnull文字区切りにし、xargs側もnull文字を区切りとして認識するようオプション指定。xargsで実行するコマンド内にパイプが含まれているのでsh -cでコマンド実行する。

$ find . -maxdepth 1 -name '*.jpeg' -print0 | xargs -0 -I @ sh -c "sips -g creation '@' | sed -e '1d'"
  creation: 2000:01:02 11:22:33
  creation: 2000:01:02 11:22:44
  creation: 2000:01:02 11:22:55
  ...

touchの仕様に合わせて日付フォーマット変更

ファイルの作成日時変更にはtouchをつかう。作成日を変更前より未来の日時に変更することはできないみたいだけど、画像ファイルの作成日時が撮影日時より過去になっていることはないはずなので実用上問題はないと思う。

日時は、次のようなyyyyMMddHHmm.ss形式でなくてはならないけれど、sipsコマンドは撮影日時を2000:01:02 11:22:33みたいな形式で出してくるので、フォーマットの変換が必要。

$ touch -t 200001021122.33 IMG_1234.jpeg

sedで正規表現をつかって次のように書いてみた。ここがもう少しシンプルになればよいのだけど。

$ echo -n '  creation: 2000:01:02 11:22:33' | awk '{print $2,$3}' | sed -E 's/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/\1\2\3\4\5.\6/g'

これをsh -cの中に組み込み。バックスラッシュはエスケープが必要になる。

$ find . -maxdepth 1 -name '*.jpeg' -print0 | xargs -0 -I @ sh -c "sips -g creation '@' | sed -e '1d' |  awk '{print \$2,\$3}' | sed -E 's/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/\\1\\2\\3\\4\\5.\\6/g'"

最後にtouch。パイプで送られてくる文字列をタイムスタンプとしてオプションに指定する必要があるので、ここでもxargsを使う。下記のような感じ。

$ echo '200001021122.33' | xargs -I TS touch -t TS IMG_1234.jpeg

これもsh -cの中に組み込むと、最初に紹介したコマンドが完成する。

find . -maxdepth 1 -name '*.jpeg' -print0 | xargs -0 -I @ sh -c "sips -g creation '@' | sed -e '1d' |  awk '{print \$2,\$3}' | sed -E 's/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/\\1\\2\\3\\4\\5.\\6/g' | xargs -I TS touch -t TS '@'"

touchでうまくいかないときのためのSetFile利用バージョン

find . -maxdepth 1 -name '*.jpeg' -print0 | xargs -0 -I @ sh -c "sips -g creation '@' | sed -e '1d' |  awk '{print \$2,\$3}' | sed -E 's/([0-9]+):([0-9]+):([0-9]+) ([0-9]+):([0-9]+):([0-9]+)/\\2\\/\\3\\/\\1 \\4:\\5:\\6/g' | xargs -I TS SetFile -d 'TS' '@'"