프로세스 교체를 사용할 때 종료 코드를 올바르게 캡처하고 오류를 처리하는 방법은 무엇입니까?

프로세스 교체를 사용할 때 종료 코드를 올바르게 캡처하고 오류를 처리하는 방법은 무엇입니까?

다음을 사용하여 파일 이름을 배열로 구문 분석하는 스크립트가 있습니다.SO에 대한 Q&A:

unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
    ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)

이는 매우 효율적이며 모든 유형의 파일 이름 변형을 완벽하게 처리합니다. 그러나 때때로 존재하지 않는 스크립트에 파일을 전달합니다. 예를 들면 다음과 같습니다.

$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...

일반적인 상황에서는 스크립트에서 유사한 종료 코드를 캡처하고 RET=$?이를 사용하여 진행 방법을 결정하게 됩니다. 위의 프로세스 대체에서는 작동하지 않는 것 같습니다.

이 상황에서 올바른 절차는 무엇입니까? 반환 코드를 캡처하는 방법은 무엇입니까? 교체 프로세스 중에 문제가 발생했는지 확인하는 더 적절한 방법이 있습니까?

답변1

표준 출력에 반환 값을 반영하여 하위 쉘 프로세스의 반환 값을 쉽게 얻을 수 있습니다. 프로세스 교체도 마찬가지입니다.

while IFS= read -r -d $'\0' FILE || 
    ! return=$FILE
do    ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")

내가 실행하면 마지막 줄은 -(또는 \0적절하게 구분된 부분)반품 상태 는 입니다 find. readEOF를 받으면 1을 반환합니다. 따라서 $return설정되는 유일한 시간은 $FILE정보의 마지막 비트를 읽을 때입니다.

나는 여분의 줄을 printf추가하는 것을 피 하곤 했습니다. 이는 NUL로 구분되지 않은 일반 실행 에서도 방금 읽은 데이터가 한 줄로 끝나지 않으면 0이 아닌 값을 반환하기 \n때문에 중요합니다 . 따라서 마지막 줄이 ewline으로 끝나지 않으면 변수로 읽은 마지막 값이 반환 값이 됩니다.read\0\n\n

위 명령을 실행한 후 다음을 수행하십시오.

echo "$return"

산출

0

공정교환부품을 바꾸면..

...
done < <(! find . -type f -print0; printf "$?")
echo "$return"

산출

1

더 간단한 데모:

printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done

산출

pipe

실제로 반환하려는 내용이 프로세스 교체(또는 그런 식으로 읽은 하위 쉘 프로세스)에서 stdout에 마지막으로 쓴 것인 한, 그런 일이 $FILE발생하면 항상 원하는 반환 상태가 전달됩니다. . 따라서 이 || ! return=...섹션은 꼭 필요한 것은 아닙니다. 개념을 설명하는 데에만 사용됩니다.

답변2

사용하다공동 프로세스. coproc내장 함수를 사용하면 하위 프로세스를 시작하고, 출력을 읽고, 종료 상태를 확인할 수 있습니다.

coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?

디렉터리가 존재하지 않으면 wait0이 아닌 상태 코드로 종료합니다.

현재 PID는 $LS_PID호출되기 전에 설정 해제되므로 다른 변수에 복사해야 합니다. wait바라보다Bash는 coproc을 기다리기 전에 *_PID 변수를 설정 해제합니다.더 알아보기.

답변3

프로세스 교체의 프로세스는 비동기식입니다. 셸은 해당 프로세스를 시작한 다음 종료 시점을 감지할 수 있는 방법을 제공하지 않습니다. 따라서 종료 상태를 얻을 수 없습니다.

종료 상태를 파일에 쓸 수 있지만 파일이 언제 쓰여졌는지 알 수 없기 때문에 이는 종종 서투른 작업입니다. 여기서 파일은 루프가 끝난 직후에 작성되므로 기다리는 것이 합리적입니다.

… < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)

또 다른 방법은 명명된 파이프와 백그라운드 프로세스(사용 가능 wait)를 사용하는 것입니다.

mkfifo find_pipe
find … >find_pipe &
find_pid=$!
… <find_pipe
wait $find_pid
find_status=$?

두 가지 접근 방식 모두 적합하지 않다면 Perl, Python, Ruby와 같은 좀 더 강력한 언어를 사용해야 할 것 같습니다.

답변4

한 가지 방법은 다음과 같습니다.

status=0
token="WzNZY3CjqF3qkasn"    # some random string
while read line; do
    if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
        status="${BASH_REMATCH[1]}"
    else
        echo "$line"
    fi
done < <(command; echo "$token:$?")
echo "Return code: $status"

아이디어는 명령이 완료된 후 임의의 토큰과 함께 종료 상태를 에코한 다음 bash 정규식을 사용하여 종료 상태를 찾아 추출하는 것입니다. 이 태그는 출력에서 ​​찾을 고유 문자열을 만드는 데 사용됩니다.

이것은 일반적인 프로그래밍 측면에서 가장 좋은 방법은 아니지만 아마도 bash에서 처리하는 데 가장 덜 고통스러운 방법일 것입니다.

관련 정보