並列化したいbash抽出機能があります。その使命は、入れ子になったアーカイブを見つけて抽出することです。理想的には、if評価とすべての対応するタスクをバックグラウンドで送信したいと思います。問題は、if評価の作業項目を順番に完了する必要があるため、「&」だけを追加できないことです。 ifのコマンドに。完全なif評価を単一のバックグラウンドジョブにカプセル化し、コマンドが順次実行されるようにする方法はありますか?
現在動作している機能は次のとおりです。
extract () {
IFS=$'\n'
trap exit SIGINT SIGTERM
for ext in zip rar tar.gz tar.bz2 tbz tgz 7z tar; do
while [ "`find . -type f -iname "*.$ext" | wc -l`" -gt 0 ]; do
for z in `find . -type f -iname "*.$ext"`; do
if [ ! -d "`echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev`" ]; then
echo "Extracting `basename "$z"` ..."
mkdir -p `echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev`
if [[ "$z" =~ ^.*\.7z$ ]]; then 7z x "$z" -o"`echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev`" > /dev/null
elif [[ "$z" =~ ^.*\.zip$ ]]; then unzip -uoLq "$z" -d `echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev` 2>&1 | grep -ive warning
elif [[ "$z" =~ ^.*\.tar\.xz$ ]] || [[ "$z" =~ ^.*\.tar\.gz$ ]] || [[ "$z" =~ ^.*\.tar\.bz2$ ]] || [[ "$z" =~ ^.*\.tgz$ ]] || [[ "$z" =~ ^.*\.tbz$ ]] || [[ "$z" =~ ^.*\.tar$ ]] ; then tar -xaf "$z" -C `echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev`
elif [[ "$z" =~ ^.*\.rar$ ]]; then unrar x -y -o+ "$z" `echo "$z" | rev | cut -c$(expr ${#ext} + 2)- | rev`
fi
rm -f "$z"
else echo "Omitting `basename "$z"`, directory with that name already exists."; rm -f "$z"
fi
done
done
done
}
そして、ソースアーカイブを削除せずに抽出を実行する方法があるかどうか疑問に思います。現時点では、無限ループを防ぐためにこれを行います。現時点では、この機能は十分に安定しているため、データが失われることはありません。しかし、安全のために何も削除しないことをお勧めします。
答え1
同じfindコマンドを複数回実行するのはなぜですか?二重各拡張ごとに?ディレクトリツリーを1回だけ移動するfindコマンドを生成できます。
EXT_REGEX='.*(zip|rar|tar.gz|tar.bz2|tbz|tgz|7z|tar)$'
find . -regextype posix-egrep -iregex $EXT_REGEX
これで、ネストされたループはまったく必要なく、while
無限ループの問題を引き起こすネストされたループも必ずしも必要ではありません。
次に、ファイル名にスペースが含まれているとコードが破損します。追加すると、この問題を解決できます。
IFS=''
(for z in ...
空間内の出力分割を停止します。)
最後に、&
各分岐の末尾に1つを追加すると、if/elif
並列に実行されます。
ところで、echo "$z" | rev
これらすべてが何を達成することになっていますか?何とか複数行のファイル名を取得していますか?
答え2
@Uselessと@Orionの提案のおかげで、その機能をコミットしました。これで、バックグラウンドですべての抽出を作成し、ソースファイルを削除せず、以前のバージョンより25%以上高速になります。 @Gillesは、並列化は保存コストがかなり高くなるため、誰にも適していないと指摘しました。しかし、私にとってはこれがうまく機能し、このスクリプトを使用できる場合は以下に含めます。
extract () { # Extracts all archives and any nested archives of specified directory into a new child directory named after the archive.
IFS=$'\n'
trap "rm $skipfiles ; exit" SIGINT SIGTERM
shopt -s nocasematch # Allows case-insensitive regex matching
echo -e "\n=====Extracting files====="
skipfiles=`mktemp` ; echo -e '\e' > $skipfiles # This creates a temp file to keep track of files that are already processed. Because of how it is read by grep, it needs an initial search string to omit from the found files. I opted for a literal escape character because who would name a file with that?
while [ "`find "$1/" -type f -regextype posix-egrep -iregex '^.*\.(tar\.gz|tar\.bz2|tar\.xz|tar|tbz|tgz|zip|rar|7z)$' | grep -ivf $skipfiles | wc -l`" -gt 0 ]; do #The while loop ensures that nested archives will be extracted. Its find operation needs to be separate from the find for the for loop below because it will change.
for z in `find "$1/" -type f -regextype posix-egrep -iregex '^.*\.(tar\.gz|tar\.bz2|tar\.xz|tar|tbz|tgz|zip|rar|7z)$' | grep -ivf $skipfiles`; do
destdir=`echo "$z" | sed -r 's/\.(tar\.gz|tar\.bz2|tar\.xz|tar|tbz|tgz|zip|rar|7z)$//'` # This removes the extension from the source filename so we can extract the files to a new directory named after the archive.
if [ ! -d "$destdir" ]; then
echo "Extracting `basename $z` into `basename $destdir` ..."
mkdir -p "$destdir"
if [[ "$z" =~ ^.*\.7z$ ]]; then 7z x "$z" -o"$destdir" > /dev/null &
elif [[ "$z" =~ ^.*\.rar$ ]]; then unrar x -y -o+ "$z" "$destdir" &
elif [[ "$z" =~ ^.*\.zip$ ]]; then unzip -uoLq "$z" -d "$destdir" 2>/dev/null &
elif [[ "$z" =~ ^.*\.(tar\.gz|tar\.bz2|tar\.xz|tar|tbz|tgz)$ ]] ; then tar -xaf "$z" -C "$destdir" &
fi
echo `basename "$z"` >> $skipfiles # This adds the name of the extracted file to the omission list for the next pass.
else echo "Omitting `basename $z`, directory with that name already exists."; echo `basename "$z"` >> $skipfiles # Same as last line
fi
done
wait # This will wait for all files in this pass to finish extracting before the next one.
done
rm "$skipfiles" # Removes temporary file
}