PIPE와 함께 사용하기 위한 Bash 종료 상태

PIPE와 함께 사용하기 위한 Bash 종료 상태

파이프를 사용할 때 종료 상태를 전달하는 방법을 이해하려고 합니다. which존재하지 않는 프로그램을 찾는 데 사용한다고 가정해 보겠습니다 .

which lss
echo $?
1

내 종료 상태는 which찾기 실패 로 인해 1입니다. lss이것은 좋다. 그러나 다음을 시도하면 :

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

답변2

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

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

관로

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

또는 명령을 사용하여 파이프라인 실패를 설정 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

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

관련 정보