PIPE와 함께 사용되는 Bash 종료 상태

PIPE와 함께 사용되는 Bash 종료 상태

파이프를 사용할 때 종료 상태가 어떻게 전달되는지 이해하려고 합니다. which존재하지 않는 프로그램을 찾기 위해 다음을 사용한다고 가정해 보겠습니다 .

which lss
echo $?
1

which위치를 찾지 못했기 때문에 lss종료 상태가 1이 되었습니다. 괜찮습니다. 그러나 다음을 시도하면 :

which lss | echo $?
0

이는 실행된 마지막 명령이 정상적으로 종료되었음을 나타냅니다. 내가 이해할 수 있는 유일한 방법은 아마도 PIPE도 종료 상태를 생성한다는 것입니다. 이렇게 이해하는 게 맞는 걸까요?

답변1

파이프의 종료 상태는 오른쪽 명령의 종료 상태입니다. 왼쪽 명령의 종료 상태는 무시됩니다.

( which lss | echo $?이 항목은 표시되지 않습니다. which lss | true; echo $?이를 표시하려면 실행해야 합니다. 에서는 which lss | echo $?echo $?파이프라인 이전의 마지막 명령 상태를 보고합니다.)

쉘이 이런 방식으로 동작하는 이유는 왼쪽의 오류를 무시해야 하는 매우 일반적인 시나리오가 있기 때문입니다. 왼쪽이 여전히 쓰고 있는 동안 오른쪽이 나가면(또는 더 일반적으로는 표준 입력을 닫는 경우) 왼쪽은 SIGPIPE 신호를 받습니다. 이 경우 일반적으로 잘못된 것은 없습니다. 오른쪽은 데이터에 관심이 없습니다. 왼쪽의 역할이 오로지 이 데이터를 생성하는 것이라면 중지해도 괜찮습니다.

그러나 왼쪽이 SIGPIPE 이외의 다른 이유로 죽거나 왼쪽의 작업이 표준 출력에서 ​​데이터를 생성하는 것만이 아닌 경우 왼쪽의 오류는 다음과 같은 실제 오류입니다. 보고되어야 합니다.

일반 sh에서 유일한 해결책은 명명된 파이프를 사용하는 것입니다.

set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1

ksh, bash 및 zsh에서는 파이프라인의 구성 요소가 0이 아닌 상태로 종료되는 경우 0이 아닌 상태로 파이프라인을 종료하도록 셸에 지시할 수 있습니다. 다음 옵션 을 설정해야 합니다 pipefail.

  • 크쉬:set -o pipefail
  • 세게 때리다:shopt -s pipefail
  • zsh: setopt pipefail(또는 setopt pipe_fail)

PIPESTATUSmksh, bash 및 zsh에서는 마지막 파이프라인의 모든 명령 상태를 포함하는 배열인 변수 (bash, mksh) 또는 (zsh) 를 사용하여 파이프라인의 모든 구성 요소 상태를 가져올 수 있습니다 pipestatus(일반화). $?).

답변2

파이프라인의 종료 상태는 파이프라인의 마지막 명령의 종료 상태입니다(쉘 옵션이 pipefail이를 지원하는 셸에 설정되지 않은 경우). 이 경우 종료 상태는 다음으로 종료되는 파이프라인의 마지막 명령의 종료 상태가 됩니다. 0이 아닌 상태).

파이프라인의 종료 상태를 출력할 수 없습니다.안으로부터파이프라인이 실제로 실행을 완료할 때까지 해당 값이 무엇인지 알 수 있는 방법이 없기 때문입니다.

파이프라인

which lss | echo $?

표준 입력을 읽지 않기 때문에 무의미합니다 echo(파이프라인은 한 명령의 출력에서 ​​다음 명령의 입력으로 데이터를 전달하는 데 사용됩니다). 또한 echo파이프라인의 종료 상태를 인쇄하지 않지만 명령 실행의 종료 상태는 즉시 표시됩니다.~ 전에파이프라인.

$ false
$ which hello | echo $?
1
which: hello: Command not found.

$ true
$ which hello | echo $?
0
which: hello: Command not found.

이것은 더 나은 예입니다:

$ echo hello | read a
$ echo $?
0

$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1

이는 파이프라인을 다음과 함께 사용할 수도 있음을 의미합니다 if.

if gzip -dc file.gz | grep -q 'something'; then
  echo 'something was found'
fi

답변3

예, PIPE는 종료 상태를 생성합니다. PIPE 앞에 명령의 종료 코드를 원하면 다음을 사용할 수 있습니다.$PIPESTATUS

에 따르면이 스택 오버플로 Q&A, 파이프를 사용할 때 명령의 종료 상태를 인쇄하려면 다음을 수행할 수 있습니다.

your_command | echo $PIPESTATUS

또는 명령을 사용하여 Pipefail을 설정하고 set -o pipefail(설정을 해제하려면 set +o pipefail) $?대신 을 사용하십시오 $PIPESTATUS.

your_command | echo $?

이 명령은 적어도 한 번 실행한 후에만 작동합니다. 이는 PIPESTATUS다음과 같이 파이프를 사용하여 명령을 실행한 후에만 작동한다는 것을 의미합니다.

command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"

${PIPESTATUS[0]}의 경우 10개 , 의 경우 1개를 받게 됩니다 ${PIPESTATUS[1]}. 보다이 U&L Q&A자세한 내용은.

답변4

셸은 파이프라인이 실행되기 전에 명령줄을 평가합니다. $?파이프라인 이전에 실행된 마지막 명령의 값 도 마찬가지 입니다. echo자체가 시작되기 전이나 후에 시작되는지 여부 which는 중요하지 않습니다.

이제 귀하의 질문이 특히 종료 상태 전파에 관한 것이라면 다음과 같은 기본 동작을 연구할 수 있습니다.

% false | true
% echo $?
0

% true | false
% echo $?
1

보시다시피 파이프라인의 종료 상태는 마지막 명령의 종료 상태입니다.

관련 정보