私は非常に不足しているbashの知識を使用してJPG / PNGファイルをJPEG XLにバッチ変換するために作成し、bashスクリプトを持っています。これまでのところ、スクリプトは私のニーズに応じて問題なくうまく機能しています。
私が解決できない唯一の問題は、画像に「視覚的ロスレス」JPEG XLと互換性のないICCプロファイルがあることを確認する最適化の繰り返しです。
私の初期のアイデアは、FindとParallelをIFとELSEと組み合わせることでしたが、成功せず、多くの構文エラー出力が発生したため、代わりにループを使用することを選択しましたが、複数のファイルがあるフォルダで検証プロセスが遅く、時には変換自体よりも時間がかかるため、スクリプトのこの部分を最適化する方法を尋ねます。
#!/bin/bash
# create a copy of all folders and subfolder inside a path called jxl #
find . -type d -not -path "./jxl/*" -exec mkdir -p ./jxl/{} \; -exec mkdir -p ./jxl/icc/{} \;
rmdir ./jxl/jxl
rmdir ./jxl/icc/jxl
# move images with a NOT compatible icc profile to a directory called icc inside jxl path #
dir="./jxl/icc"
icc1="Device Model : "
icc2="Device Model : NONE"
icc3="Device Model : MS30"
shopt -s globstar
for f in **/*.jpg **/*.jpeg **/*.jpe **/*.png
do
check=$(exiftool -devicemodel "$f")
if [ "$check" = "$icc1" ] || [ "$check" = "$icc2" ] || [ "$check" = "$icc3" ]; then
echo "$f = icc profile NOT compatible"
mv "$f" "$dir/$f"
else
echo "$f = icc profile compatible"
fi
done
# Run cjxl encoder e ignore all files inside the jxl folder
find ./ -type f \( -iname \*.jpg -o -iname \*.jpeg -o -iname \*.jpe -o -iname \*.png \) -not -path "./jxl/*" -print0 | parallel --jobs 8 -0 cjxl '{}' './jxl/{.}.jxl' -d 1 -e 7 -E 3 -I 1 --lossless_jpeg 0\;
# copy all files that are not a image to the jxl folder
find ./ -type f \( -iname \*.* ! -iname \*.jpg ! -iname \*.jpeg ! -iname \*.jpe ! -iname \*.png ! -iname \*.sh ! -iname \*.html \) -not -path "./jxl/*" -print0 | parallel --jobs 5 -0 mv '{}' './jxl/{}' \;
#delete all empty folders inside the jxl folder
find ./jxl -type d -empty -delete
答え1
私が見ることができる唯一の明確な改善点は、外部プログラムを何度もフォークすることを避けることです。これが「修正」する価値があるかfind
どうかは、各コマンドが分岐された回数によって異なりbash
ます。何百、何千という場合は確かにそうです。
たとえば、最初の項目では、find
次を実行しています。mkdir
二重見つかった各ファイルについて。次のように作成して最適化できます。
find . -type d -not -path "./jxl/*" \
-exec bash -c 'for d; do
printf './jxl/%s\0./jxl/icc/%s\0' "$d" "$d";
done | xargs -0r mkdir -p ' bash {} +
printf
これは、使用するNULで区切られたディレクトリのリストを送信するために使用されますxargs -0r mkdir -p
。
後で、スクリプトのループで各非ICC互換ファイルに対してこれを1回実行しfor f in **/*.jpg **/*.jpeg **/*.jpe **/*.png
ます。mv
移動したいファイルを含む配列を構築しますprintf
。xargs -0r mv -t "$dir/"
たとえば、
declare -a mvfiles=()
for f in **/*.jpg **/*.jpeg **/*.jpe **/*.png
do
check=$(exiftool -devicemodel "$f")
if [ "$check" = "$icc1" ] || [ "$check" = "$icc2" ] || [ "$check" = "$icc3" ]; then
echo "$f = icc profile NOT compatible"
mvfiles+=("$f")
else
echo "$f = icc profile compatible"
fi
done
printf '%s\0' "${mvfiles[@]}" | xargs -0r mv -t "$dir/"
これは、ターゲットディレクトリを指定するためのオプションmv
と共にGNUを使用すると仮定します。-t
今後すべてのソースパス名。 'オプションをmv
使用する必要があり、これはファイル名ごとに1つのオプションを実行する目的を失うため、他のバージョンではこれを行う価値はありません。もちろん、FreeBSDバージョンのwithオプションを使用しない限り(同様に動作しますが、コマンドごとに複数の引数を受け入れます)xargs
-I
mv
xargs
-J
-I
xargs
なしで使用できますが、mv "${mvfiles[@]}" "$dir/"
何千ものファイルがあると、ARG_MAXを超える危険があります。 xargs を使用するとリスクを回避できます。
parallel
ところで、この問題をどのように処理するのかよくわかりません{}
(主に並列作業に使用しますxargs -P
)。しかし、調べてみたいかもしれません。 5つのプロセスを同時に実行したい場合は、parallel
各パラメータに対して1つのコマンドを実行します。mv
これはランタイムに必要なものかもしれませんcjxl
(私はインストールしておらず、ファイル名引数を処理する方法がわからないので説明できません)。しかし、パイプを使用して使用する方mv
が良いです。mv -t
xargs -0r mv -t ./jxl/
また、parallel
一度に実行される複数のプロセスを使用すると、期待されるパフォーマンスの向上を提供できない可能性があることも注目に値します。これは、プロセスがI / Oまたは使用可能なCPU電力によって制限されるかどうかによって異なります。
もしそうならいいえI/O 帯域幅が不足している場合、並列で実行するとパフォーマンスが大幅に向上します。
プロセスに入出力が不足すると、入力を待っている間にほとんどの時間がアイドル状態になります。特にIOのために互いに競合するようになる場合にはさらにそうです。それでは実験を通して学びましょう最適数cjxl
ハードウェアの利用可能なIO帯域幅を超えずに実行できるジョブ数 - 8可能正確な数字またはそれより少ない場合もあります。コアが8個より多ければもっと多いかもしれませんが、その可能性はほとんどありません。
答え2
GNU Parallel で呼び出される単一の bash 関数を含むようにスクリプトを変更することをお勧めします。
doit() {
# Do all processing of a single file here
# including if statements
}
export -f doit
find ... | parallel doit
これの利点は、一度に1つのファイルに対して機能をテストできることです。
もう1つの利点は、CPUとI / O集約的な部分を混在させることです。したがって、運が良ければ、あるタスクはCPUを使用し、もう一方はディスクを使用します。