
파이프를 사용할 때 종료 상태가 어떻게 전달되는지 이해하려고 합니다. 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
)
PIPESTATUS
mksh, 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
보시다시피 파이프라인의 종료 상태는 마지막 명령의 종료 상태입니다.