bash의 조건 및 백그라운드 작업인 경우 중첩 루프

bash의 조건 및 백그라운드 작업인 경우 중첩 루프

병렬화하려는 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 명령을 여러 번 실행하는 이유는 무엇입니까?두 배각 확장마다? 디렉토리 트리를 한 번만 탐색하는 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 ...공간에서 출력 분할을 중지합니다.)

마지막으로 &각 분기 끝에 하나를 추가 하면 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
}

관련 정보