find
나는 디렉토리에 많은 파일을 가져온 다음 xargs
chroot 환경에서 한 번에 1 스크립트의 해당 파일을 실행하는 데 사용하는 bash 스크립트를 가지고 있습니다 . 내 이해는 xargs가 0이 아닌 종료 코드를 확인하면 종료되고 처리를 중지하지만 어떤 이유로 든 그렇지 않은 것 같습니다.
내가 가지고 있는 스크립트는 다음과 같습니다.
#!/usr/bin/env bash
set -euo pipefail
script_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )
rootfs="$1"
exec_script() {
script="$1"
relative_script_path="$(realpath --relative-to="$script_dir" "$script")"
echo -e "\e[1;94m=> executing script $script ($relative_script_path)\e[0m"
sleep 5
if ! "$rootfs/enter-chroot" sh -c "/$relative_script_path"; then
echo -e "\e[1;31m=> script $script failed\e[0m"
exit 1
fi
echo -e "\e[1;32m=> script $script ran successfully\e[0m"
}
export -f exec_script
export rootfs
export script_dir
find "$script_dir/build/scripts" -name '*.sh' -print0 | sort -z | xargs -r -0 -I% -n1 bash -c 'exec_script "$@"' _ %
실행하면 다음과 같은 결과가 나타납니다.
./build/run.sh /tmp/test
=> executing script /tmp/builder/build/scripts/000-upgrade.sh (build/scripts/000-upgrade.sh)
environment: line 4: /tmp/test/enter-chroot: Not a directory
=> script /tmp/builder/build/scripts/000-upgrade.sh failed
=> executing script /tmp/builder/build/scripts/001-firmware.sh (build/scripts/001-firmware.sh)
environment: line 4: /tmp/test/enter-chroot: Not a directory
=> script /tmp/builder/build/scripts/001-firmware.sh failed
내가 어디서 잘못됐나요? xargs가 처리를 중지하고 0이 아닌 종료 코드로 종료되도록 하려면 어떻게 해야 합니까?
답변1
문서 xargs
( man xargs
종료에 대해 실제로 말하는 내용을 참조하세요.
이 명령의 호출이 상태 255로 종료되면
xargs
추가 입력을 읽지 않고 즉시 중지됩니다. 오류 메시지는 다음에서 전송되었습니다.표준 에러이런 일이 발생하면.
따라서 가능한 해결책 중 하나는 exec_script
오류 발생 시 종료 상태 255를 반환하도록 변경하는 것입니다.
아무것도 변경할 수 없는 경우, 또 다른 가능한 해결책 exec_script
은 일반 콘텐츠를 xargs
셸 루프로 변환하는 것입니다.
find "$script_dir/build/scripts" -name '*.sh' -print0 |
sort -z |
while IFS= read -r -d '' item && exec_script _ "$item"; do :; done
exec_script
0이 아닌 종료 값이 반환되면 루프가 중단됩니다.
또 다른 솔루션은논평이는 아마도 스크립트에서 종료 오류를 찾아 255로 바꾸는 가장 간단한 외부 수정일 것입니다.
find "$script_dir/build/scripts" -name '*.sh' -print0 |
sort -z |
xargs -r -0 -I% -n1 bash -c 'exec_script "$@" || exit 255' _ %
답변2
xargs
꽤 번거로운 일입니다. 255로 종료할 때만 중지되기 때문에 set -eo pipefail
서브쉘을 사용하여 set -eo pipefail
실패를 캡슐화하고 255
.
이 예에서는 다음 errornow
줄을 주석 처리하여 차이점을 확인합니다.
#!/bin/bash
set -x
set -eo pipefail
parallel_uploads="4"
s3_bucket_name="backup"
all_files=(
"/12.pbd"
"/13.pbd"
"/14.pbd"
"/15.pbd"
"/16.pbd"
"/17.pbd"
"/18.pbd"
"/19.pbd"
"/20.pbd"
"/21.pbd"
"/22.pbd"
"/23.pbd"
"/24.pbd"
"/25.pbd"
"/26.pbd"
"/27.pbd"
)
# Workaround for the posix shell bug they call it feature
# https://unix.stackexchange.com/questions/65532/why-does-set-e-not-work-inside-subshells-with-parenthesis-followed-by-an-or
function acually_upload_to_s3()
{
set -x;
set -eu -o pipefail;
printf 'Doing some\n';
sleeptime="$(( RANDOM % 50 + 1 ))"
sleep "$sleeptime";
erroring_some;
printf 'Doing more some\n';
}
function upload_to_s3()
{
set -x;
set -eu -o pipefail;
# https://superuser.com/questions/403263/how-to-pass-bash-script-arguments-to-a-subshell
/bin/bash -c "acually_upload_to_s3 $(printf "${1+ %q}" "$@")" || exit 255
}
function upload_all()
{
export s3_bucket_name;
export -f upload_to_s3;
export -f acually_upload_to_s3;
# https://unix.stackexchange.com/questions/566834/xargs-does-not-quit-on-error
# https://stackoverflow.com/questions/11003418/calling-shell-functions-with-xargs
# https://stackoverflow.com/questions/6441509/how-to-write-a-process-pool-bash-shell
# https://stackoverflow.com/questions/356100/how-to-wait-in-bash-for-several-subprocesses-to-finish-and-return-exit-code-0
printf "'%s'\n" "${all_files[@]}" | xargs \
--max-procs="$parallel_uploads" \
--max-args=1 \
--replace={} \
/bin/bash -c 'time upload_to_s3 "$s3_bucket_name" "{}"';
}
time upload_all \
&& printf '%s Successfully uploaded all files\n' "$(date)" \
|| printf '%s Error: Could not upload some files\n' "$(date)";
출력 예:
$ bash upload_to_s3_glacier_deep.sh
+ set -eo pipefail
+ parallel_uploads=4
+ s3_bucket_name=backup
+ all_files=("/12.pbd" "/13.pbd" "/14.pbd" "/15.pbd" "/16.pbd" "/17.pbd" "/18.pbd" "/19.pbd" "/20.pbd" "/21.pbd" "/22.pbd" "/23.pbd" "/24.pbd" "/25.pbd" "/26.pbd" "/27.pbd")
+ upload_all
+ export s3_bucket_name
+ export -f upload_to_s3
+ export -f acually_upload_to_s3
+ printf ''\''%s'\''\n' /12.pbd /13.pbd /14.pbd /15.pbd /16.pbd /17.pbd /18.pbd /19.pbd /20.pbd /21.pbd /22.pbd /23.pbd /24.pbd /25.pbd /26.pbd /27.pbd
+ xargs --max-procs=4 --max-args=1 '--replace={}' /bin/bash -c 'time upload_to_s3 "$s3_bucket_name" "{}"'
+ set -eu -o pipefail
++ printf ' %q' backup /12.pbd
+ /bin/bash -c 'acually_upload_to_s3 backup /12.pbd'
+ set -eu -o pipefail
++ printf ' %q' backup /13.pbd
+ /bin/bash -c 'acually_upload_to_s3 backup /13.pbd'
+ set -eu -o pipefail
++ printf ' %q' backup /14.pbd
+ /bin/bash -c 'acually_upload_to_s3 backup /14.pbd'
+ set -eu -o pipefail
+ printf 'Doing some\n'
Doing some
+ sleeptime=3
+ sleep 3
+ set -eu -o pipefail
++ printf ' %q' backup /15.pbd
+ /bin/bash -c 'acually_upload_to_s3 backup /15.pbd'
+ set -eu -o pipefail
+ printf 'Doing some\n'
Doing some
+ sleeptime=49
+ sleep 49
+ set -eu -o pipefail
+ printf 'Doing some\n'
Doing some
+ sleeptime=13
+ sleep 13
+ set -eu -o pipefail
+ printf 'Doing some\n'
Doing some
+ sleeptime=30
+ sleep 30
+ erroring_some
environment: line 5: erroring_some: command not found
+ exit 255
real 0m3.146s
user 0m0.045s
sys 0m0.123s
xargs: /bin/bash: exited with status 255; aborting
+ erroring_some
environment: line 5: erroring_some: command not found
+ exit 255
real 0m13.149s
user 0m0.015s
sys 0m0.123s
xargs: /bin/bash: exited with status 255; aborting
real 0m13.271s
user 0m0.075s
sys 0m0.337s
++ date
+ printf '%s Error: Could not upload some files\n' 'Fri, Nov 19, 2021 22:00:30'
Fri, Nov 19, 2021 22:00:30 Error: Could not upload some files