imagemagick の mogrify コマンドを使うと引数に与えた複数のファイルに同じ処理を行って、結果を同名のファイルに保存することができる。例えば以下は、カレントディレクトリ内の *.JPG にマッチするファイルを処理する例。この例では -quality オプションで品質を変えている。このような処理を行う場合、適当な品質 (ここでは 40) が決まるまでは元ファイルを残したうえで何回か処理を行いたいという要求がある。このような場合、下の例のように mogrify を使うのはよくない。
$ mogrify -verbose -quality 40 -strip *.jpg
mogrify を使って出力ファイル名を変えるには以下のようにする。要は本来フォーマットを変えるために用意されている -format オプションを使って出力ファイル名を変えるわけだ。-format オプションの値を "converted" のように imagemagick がサポートしないフォーマットにすることで、フォーマット変換はされずに -quality 40 -strip だけが行われる。"converted.png" のようにすれば png にフォーマット変換も行われる。つまり、"converted.jpg" のようにすれば便利。ここでの問題は、変換後のファイル名規則に制限があるということ。たとえば元ファイル名の先頭に converted という目印をつけたいとか、元ファイルの拡張子を保存しつつファイル名を変更したいという用途には向かない。
$ mogrify -verbose -format converted -quality 40 -strip *.jpg $ mogrify -verbose -format converted.png -quality 40 -strip *.jpg $ mogrify -list format
元ファイル名の先頭に converted という目印をつけるには以下のようにする。ここでは mogrify は使わず convert コマンドを使っている。要は find でマッチするファイルを選んで、各ファイルから出力ファイル名を作って、convert に渡している。また、find を使うことでカレントディレクトリだけでなく、カレント以下のディレクトリすべてに含まれるjpgファイルについて処理が行われるようになる。
$ find ./ -type f -name '*.jpg' -execdir sh -ec "f={}; convert -verbose \${f##*/} -quality 40 -strip converted_\${f##*/}" \;
さて、mogrify を使った場合、find+convert を使った場合の両方とも、すでに出力ファイル名と同じファイル名のファイルが存在した場合を考慮していない。このままでは、元ファイルが上書きされてしまう可能性がある。これを解決するには以下のようにする。要は出力先をすべてあるディレクトリ (mktemp で作成したディレクトリ) の下にしてしまう。この場合、出力されるファイルはすべて新しいディレクトリの下に、対象のディレクトリツリーを反映した形で作成される。
$ d=`mktemp -d ../test_converted.XXXXXX` && find ./ -type f -name '*.jpg' -exec sh -ec "f={}; mkdir -p ${d}/\${f%/*}; convert -verbose \${f} -quality 40 -strip ${d}/\${f};" \;
これで完全にfind が見つけるファイルの順番によらず結果が同じになるので、各ファイルの処理を並列化したくなる。xargs を使って 2 並列でこれを行うには以下。メモリと CPU に余裕がある人向け。
$ d=`mktemp -d ../test_converted.XXXXXX` && find ./ -type f -name '*.jpg' -print0 | xargs -0 -r -n 1 -P 2 -I {} sh -ec "f={}; mkdir -p ${d}/\${f%/*}; convert -verbose \${f} -geometry 50% -strip ${d}/\${f};"