프로세스 대체를 사용할 때 종료 코드를 캡처하고 오류를 올바르게 처리하려면 어떻게 해야 합니까?

프로세스 대체를 사용할 때 종료 코드를 캡처하고 오류를 올바르게 처리하려면 어떻게 해야 합니까?

다음에서 가져온 방법을 사용하여 파일 이름을 배열로 구문 분석하는 스크립트가 있습니다.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추가 ewline을 추가하지 않으려고 했습니다 . 이는 정기적으로 수행되는 작업(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이 아닌 상태 코드로 종료됩니다.

호출되기 $LS_PID전에 설정이 해제되므로 현재 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에서 처리하는 가장 고통스러운 방법일 수 있습니다.

관련 정보