쉘 파이프에서 오류 코드 포착
현재 다음과 같은 스크립트가 있습니다.
./a | ./b | ./c
a, b 또는 c 중 하나가 오류 코드와 함께 종료되면 오류 메시지를 인쇄하고 잘못된 출력을 앞으로 파이프하는 대신 중지하도록 수정하고 싶습니다.
그렇게하는 가장 간단하고 깨끗한 방법은 무엇입니까?
첫 번째 명령이 성공할 때까지 두 번째 명령을 진행하지 않으려면 임시 파일을 사용해야합니다. 그 간단한 버전은 다음과 같습니다.
tmp=${TMPDIR:-/tmp}/mine.$$
if ./a > $tmp.1
then
if ./b <$tmp.1 >$tmp.2
then
if ./c <$tmp.2
then : OK
else echo "./c failed" 1>&2
fi
else echo "./b failed" 1>&2
fi
else echo "./a failed" 1>&2
fi
rm -f $tmp.[12]
'1> & 2'리디렉션은 '> & 2'로 축약 될 수도 있습니다. 그러나 이전 버전의 MKS 셸은 앞의 '1'없이 오류 리디렉션을 잘못 처리했기 때문에 오랫동안 안정성을 위해 모호하지 않은 표기법을 사용했습니다.
방해하면 파일이 유출됩니다. 방폭형 (약간) 쉘 프로그래밍은 다음을 사용합니다.
tmp=${TMPDIR:-/tmp}/mine.$$
trap 'rm -f $tmp.[12]; exit 1' 0 1 2 3 13 15
...if statement as before...
rm -f $tmp.[12]
trap 0 1 2 3 13 15
첫 번째 트랩 행은 rm -f $tmp.[12]; exit 1
신호 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE 또는 15 SIGTERM이 발생할 때 '명령 실행' ' 이라고 말 하거나 0 (어떤 이유로 든 쉘이 종료되는 경우)입니다. 쉘 스크립트를 작성하는 경우 최종 트랩은 쉘 종료 트랩 인 0의 트랩 만 제거하면됩니다 (프로세스가 종료 되려고하므로 다른 신호는 그대로 둘 수 있습니다).
원래 파이프 라인에서는 'a'가 완료되기 전에 'c'가 'b'에서 데이터를 읽는 것이 가능합니다. 이는 일반적으로 바람직합니다 (예를 들어 여러 코어가 수행 할 작업을 제공함). 'b'가 '정렬'단계이면 적용되지 않습니다. 'b'는 출력을 생성하기 전에 모든 입력을 확인해야합니다.
실패한 명령을 감지하려면 다음을 사용할 수 있습니다.
(./a || echo "./a exited with $?" 1>&2) |
(./b || echo "./b exited with $?" 1>&2) |
(./c || echo "./c exited with $?" 1>&2)
이것은 간단하고 대칭 적입니다. 4- 파트 또는 N- 파트 파이프 라인으로 확장하는 것은 간단합니다.
'set -e'를 사용한 간단한 실험은 도움이되지 않았습니다.
bash 에서는 파일 시작 부분에 set -e
및 set -o pipefail
을 사용할 수 있습니다 . ./a | ./b | ./c
세 스크립트 중 하나라도 실패하면 후속 명령 이 실패합니다. 반환 코드는 첫 번째 실패한 스크립트의 반환 코드입니다.
참고 pipefail
표준에서 사용할 수 없습니다 쉬 .
${PIPESTATUS[]}
전체 실행 후 배열을 확인할 수도 있습니다 ( 예 : 다음을 실행하는 경우).
./a | ./b | ./c
그런 다음 ${PIPESTATUS}
파이프에있는 각 명령의 오류 코드 배열이 있으므로 중간 명령이 실패하면 echo ${PIPESTATUS[@]}
다음과 같은 내용이 포함됩니다.
0 1 0
다음과 같은 명령이 실행됩니다.
test ${PIPESTATUS[0]} -eq 0 -a ${PIPESTATUS[1]} -eq 0 -a ${PIPESTATUS[2]} -eq 0
will allow you to check that all commands in the pipe succeeded.
Unfortunately, the answer by Johnathan requires temporary files and the answers by Michel and Imron requires bash (even though this question is tagged shell). As pointed out by others already, it is not possible to abort the pipe before later processes are started. All processes are started at once and will thus all run before any errors can be communicated. But the title of the question was also asking about error codes. These can be retrieved and investigated after the pipe finished to figure out whether any of the involved processes failed.
Here is a solution that catches all errors in the pipe and not only errors of the last component. So this is like bash's pipefail, just more powerful in the sense that you can retrieve all the error codes.
res=$( (./a 2>&1 || echo "1st failed with $?" >&2) |
(./b 2>&1 || echo "2nd failed with $?" >&2) |
(./c 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1)
if [ -n "$res" ]; then
echo pipe failed
fi
To detect whether anything failed, an echo
command prints on standard error in case any command fails. Then the combined standard error output is saved in $res
and investigated later. This is also why standard error of all processes is redirected to standard output. You can also send that output to /dev/null
or leave it as yet another indicator that something went wrong. You can replace the last redirect to /dev/null
with a file if yo uneed to store the output of the last command anywhere.
To play more with this construct and to convince yourself that this really does what it should, I replaced ./a
, ./b
and ./c
by subshells which execute echo
, cat
and exit
. You can use this to check that this construct really forwards all the output from one process to another and that the error codes get recorded correctly.
res=$( (sh -c "echo 1st out; exit 0" 2>&1 || echo "1st failed with $?" >&2) |
(sh -c "cat; echo 2nd out; exit 0" 2>&1 || echo "2nd failed with $?" >&2) |
(sh -c "echo start; cat; echo end; exit 0" 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1)
if [ -n "$res" ]; then
echo pipe failed
fi
참고URL : https://stackoverflow.com/questions/1550933/catching-error-codes-in-a-shell-pipe
'programing tip' 카테고리의 다른 글
IEqualityComparer를 사용하는 방법 (0) | 2020.09.05 |
---|---|
Notepad ++에서 플러그인 관리자를 보는 방법 (0) | 2020.09.05 |
서블릿 기반 웹 애플리케이션에서 백그라운드 작업을 실행하는 방법은 무엇입니까? (0) | 2020.09.05 |
NUnit과 유사한 xUnit.net의 테스트 매개 변수화 (0) | 2020.09.05 |
Python 3.5에서 코 루틴과 미래 / 작업의 차이점은 무엇입니까? (0) | 2020.09.04 |