쉘 스크립트를 사용하여 실행된 명령이 실패할 때마다 실패하기를 원합니다.
보통 나는 이렇게 한다:
set -e
set -o pipefail
(보통 저도 set -u
그렇게 추가해요)
문제는 위의 방법 중 어느 것도 프로세스 교체에 효과가 없다는 것입니다. 이 코드는 "ok"를 인쇄하고 반환 코드 = 0으로 종료됩니다. 반면 저는 실패하기를 원합니다.
#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)
프로세스 교체 외에 "파이프 실패"에 해당하는 것이 있습니까? 명령의 출력을 파일로 전달하지만 이러한 프로그램이 실패할 때마다 오류를 발생시키는 다른 방법이 있습니까?
가난한 사람의 해결책은 이러한 명령이 stderr에 기록되는지 여부를 감지하는 것입니다(그러나 일부 명령은 성공할 경우 stderr에 기록합니다).
또 다른 posix 호환 솔루션은 명명된 파이프를 사용하는 것이지만 컴파일된 코드에서 동적으로 구축된 단일 라이너로 프로세스 대체를 사용하여 해당 명령을 시작해야 하며 명명된 파이프를 생성하면 작업이 복잡해집니다(추가 명령, 오류 포착). 삭제 등 )
답변1
이 문제는 다음 방법으로만 해결할 수 있습니다.
cat <(false || kill $$) <(echo ok)
other_command
SIGTERM
스크립트의 하위 쉘은 두 번째 명령을 실행하기 전에 ( )를 완료합니다 other_command
. 명령은 echo ok
"때때로" 실행됩니다. 문제는 프로세스 교체가 비동기식이라는 것입니다. kill $$
명령이 실행된다는 보장은 없습니다.앞으로또는뒤쪽에주문하다 echo ok
. 이것은 운영 체제 스케줄링 문제입니다.
다음 bash 스크립트를 고려해보세요.
#!/bin/bash
set -e
set -o pipefail
cat <(echo pre) <(false || kill $$) <(echo post)
echo "you will never see this"
이 스크립트의 출력은 다음과 같습니다.
$ ./script
Terminated
$ echo $?
143 # it's 128 + 15 (signal number of SIGTERM)
또는:
$ ./script
Terminated
$ pre
post
$ echo $?
143
시도해 볼 수 있습니다. 몇 번 시도하면 출력에 두 가지 다른 순서가 표시됩니다. 첫 번째 명령에서는 echo
다른 두 명령이 파일 설명자에 쓰기 전에 스크립트가 종료됩니다. 두 번째 명령에서는 false
또는 kill
명령이 echo
명령 뒤에 예약될 수 있습니다.
또는 더 정확하게는 쉘 프로세스에 신호를 보내는 유틸리티의 시스템 호출이 signal()
echo 시스템 호출보다 늦게 또는 일찍 예약(또는 전달)됩니다.kill
SIGTERM
write()
그러나 스크립트가 중지되고 종료 코드가 0이 아닙니다. 그래서 그것은 당신의 문제를 해결해야합니다.
또 다른 솔루션물론 이를 위해서는 명명된 파이프를 사용합니다. 그러나 명명된 파이프나 위의 해결 방법을 구현하는 스크립트가 얼마나 복잡한지에 따라 달라집니다.
인용하다:
답변2
참고로 답변과 의견이 좋고 도움이 되었음에도 불구하고 결국 다른 것을 구현하게 되었습니다. (부모 프로세스에서 신호를 수신하는 데 몇 가지 제한 사항이 있었는데 질문에서 언급하지 않았습니다.)
기본적으로 나는 다음과 같이 끝났습니다.
command <(subcomand 2>error_file && rm error_file) <(....) ...
그런 다음 오류 파일을 확인합니다. 존재하는 경우 어떤 하위 명령이 실패했는지 알 수 있습니다(그리고 error_file의 내용이 유용할 수 있음). 처음에 원했던 것보다 더 장황하고 해킹적이지만 한 줄의 bash 명령으로 명명된 파이프를 만드는 것보다 간단합니다.
답변3
이 예에서는 kill
와 함께 사용하는 방법을 보여줍니다 trap
.
#! /bin/bash
failure ()
{
echo 'sub process failed' >&2
exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )
그러나 kill
하위 프로세스에서 상위 프로세스로 반환 코드를 전달할 방법이 없습니다.
답변4
내가 찾은 가장 신뢰할 수 있는 방법은 함수 컨텍스트에서와 마찬가지로 하위 프로세스의 오류 코드를 임시 파일에 저장하는 것입니다.
function() {
ERR=$(mktemp)
VAR=$(some_command; echo $? > $ERR)
if [[ $(cat $ERR && rm $ERR) -gt 0 ]]; then
echo "An error occurred in the sub shell" > /dev/stderr
return 1
fi
}