複数のファイルでスクリプトをすばやく実行するには?

複数のファイルでスクリプトをすばやく実行するには?

私は非常に不足している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移動したいファイルを含む配列を構築しますprintfxargs -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-Imvxargs-J-I

xargsなしで使用できますが、mv "${mvfiles[@]}" "$dir/"何千ものファイルがあると、ARG_MAXを超える危険があります。 xargs を使用するとリスクを回避できます。

parallelところで、この問題をどのように処理するのかよくわかりません{}(主に並列作業に使用しますxargs -P)。しかし、調べてみたいかもしれません。 5つのプロセスを同時に実行したい場合は、parallel各パラメータに対して1つのコマンドを実行します。mvこれはランタイムに必要なものかもしれませんcjxl(私はインストールしておらず、ファイル名引数を処理する方法がわからないので説明できません)。しかし、パイプを使用して使用する方mvが良いです。mv -txargs -0r mv -t ./jxl/


また、parallel一度に実行される複数のプロセスを使用すると、期待されるパフォーマンスの向上を提供できない可能性があることも注目に値します。これは、プロセスがI / Oまたは使用可能なCPU電力によって制限されるかどうかによって異なります。

もしそうならいいえI/O 帯域幅が不足している場合、並列で実行するとパフォーマンスが大幅に向上します。

プロセスに入出力が不足すると、入力を待っている間にほとんどの時間がアイドル状態になります。特にIOのために互いに競合するようになる場合にはさらにそうです。それでは実験を通して学びましょう最適数cjxlハードウェアの利用可能なIO帯域幅を超えずに実行できるジョブ数 - 8可能正確な数字またはそれより少ない場合もあります。コアが8個より多ければもっと多いかもしれませんが、その可能性はほとんどありません。

おすすめ記事