프로세스 대체를 사용할 때 종료 코드를 캡처하고 오류를 올바르게 처리하려면 어떻게합니까? . unset ARGS ARGID=”1″ while IFS= read -r -d $’\0′

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=$?진행 방법을 결정하는 데 사용합니다. 위의 프로세스 대체에서는 작동하지 않는 것 같습니다.

이와 같은 경우 올바른 절차는 무엇입니까? 리턴 코드를 어떻게 캡처 할 수 있습니까? 대체 프로세스에서 문제가 발생했는지 확인하는 다른 적절한 방법이 있습니까?



답변

stdout을 통해 리턴을 에코하여 서브 쉘 프로세스에서 리턴을 쉽게 얻을 수 있습니다. 프로세스 대체도 마찬가지입니다.

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여분의 \newline 을 추가하는 것을 피하기 위해 사용 합니다. 이것은 중요합니다. read정기적으로 수행 된 조차도-NUL을 제한하지 않는- 조차도 \0읽은 데이터가 끝나지 않는 경우 0 이외의 값을 반환 하기 때문에 중요 합니다 \newline. 따라서 마지막 줄이 \newline으로 끝나지 않으면 읽은 변수의 마지막 값이 반환됩니다.

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

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=...부분은 꼭 필요한 것은 아닙니다. 개념을 설명하는 데만 사용됩니다.


답변

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

종료 상태를 파일에 쓸 수는 있지만 파일이 언제 작성되는지 알 수 없기 때문에 일반적으로 어색합니다. 여기서 파일은 루프가 끝난 직후에 작성되므로 기다릴 수 있습니다.

 < <(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와 같이 더 유능한 언어를 사용해야 할 것입니다.


답변

coprocess를 사용하십시오 . 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. 자세한 내용 은 coproc기다릴 수 있기 전에 Bash unsets * _PID 변수 를 참조하십시오.


답변

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

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에서 처리하는 것이 가장 고통스러운 방법 일 수 있습니다.


답변