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


13

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

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

답변:


5

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


5

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

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

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


이 답변에 감사드립니다. 설명 된 방법은 잘 작동하지만 예상보다 약간 더 복잡하다는 것을 인정해야합니다. 내 경우에는 모든 인수를 반복하고 파일 또는 폴더가 아닌 경우 오류를 인쇄하는 질문에 표시된 루프보다 먼저 루프를 설정했습니다. 대체 프로세스에서 발생할 수있는 다른 유형의 오류는 처리하지 않지만이 특정 경우에는 충분합니다. 이와 같은 상황에서보다 정교한 오류 처리 방법이 필요한 경우 분명히 답으로 돌아올 것입니다.
Glutanimate

2

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 변수 를 참조하십시오.


1
<& "$ LS"와 read -u $ LS를 언제 사용하는지 궁금합니다. - 감사
Brian Chrisman

1
@BrianChrisman이 경우에는 절대로 그렇지 않습니다. read -u잘 작동합니다. 이 예는 일반적이며 코 프로세스의 출력을 다른 명령으로 파이프하는 방법을 보여줍니다.
Feuermurmel

1

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.