bash : 프로세스 대체에서 오류를 전파하는 방법? 않는다는 것입니다. 이 코드는 “ok”를 출력하고 리턴

명령으로 실행될 때마다 쉘 스크립트가 실패하기를 원합니다.

일반적으로 나는 다음과 같이합니다.

set -e
set -o pipefail

(일반적으로 나는 set -u또한 추가 )

문제는 위의 어느 것도 프로세스 대체와 함께 작동하지 않는다는 것입니다. 이 코드는 “ok”를 출력하고 리턴 코드 = 0으로 종료하지만 실패하고 싶습니다.

#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)

“pipefail”과 동일하지만 프로세스 대체를위한 것이 있습니까? 파일처럼 명령의 출력을 명령에 전달하는 다른 방법이지만 해당 프로그램이 실패 할 때마다 오류가 발생합니까?

가난한 사람의 솔루션은 해당 명령이 stderr에 기록되는지 감지하지만 (일부 명령은 성공적인 시나리오에서 stderr에 기록)

posix를 준수하는 또 다른 솔루션은 명명 된 파이프를 사용하는 것이지만 컴파일 된 코드에서 즉시 빌드 된 oneliners로 사용하는 프로세스 대체 명령을 시작해야하며 명명 된 파이프를 만들면 문제가 복잡해집니다 (추가 명령, 트래핑 오류) 삭제 등)



답변

예를 들어 다음과 같은 문제로 해결할 수 있습니다.

cat <(false || kill $$) <(echo ok)
other_command

스크립트의 서브 쉘은 SIGTERM두 번째 명령이 실행되기 전에 d입니다 ( 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()kill상기 전송 utillity SIGTERM쉘 프로세스 신호 또는 이상 에코 이전 예약 된 (또는 전송 된) write()콜.

그러나 스크립트가 중지되고 종료 코드가 0이 아니므로 문제를 해결해야합니다.

물론 다른 해결책 은 명명 된 파이프를 사용하는 것입니다. 그러나 명명 된 파이프 또는 위의 해결 방법을 구현하는 것이 얼마나 복잡한지는 스크립트에 따라 다릅니다.

참고 문헌 :


답변

기록을 위해, 답변과 의견이 좋았고 도움이 되더라도 약간 다른 것을 구현하지 않았습니다 (상위 프로세스에서 언급하지 않은 부모 프로세스에서 신호 수신에 대한 제한이있었습니다)

기본적으로 나는 다음과 같은 일을 끝내었다.

command <(subcomand 2>error_file && rm error_file) <(....) ...

그런 다음 오류 파일을 확인합니다. 존재하는 경우 어떤 하위 명령이 실패했는지 알고 있으며 error_file의 내용이 유용 할 수 있습니다. 원래 원했던 것보다 더 장황하고 해킹이 있지만 한 줄짜리 bash 명령으로 명명 된 파이프를 만드는 것보다 번거롭지 않습니다.


답변

이 예는와 kill함께 사용하는 방법을 보여줍니다 trap.

#! /bin/bash
failure ()
{
  echo 'sub process failed' >&2
  exit 1
}
trap failure SIGUSR1
cat < <( false || kill -SIGUSR1 $$ )

그러나 kill하위 프로세스에서 상위 프로세스로 리턴 코드를 전달할 수 없습니다.


답변

POSIX 쉘을 지원하지 않는 / 구현$PIPESTATUS$pipestatus 하는 것과 비슷한 방식으로 파이프를 통해 명령을 전달하여 명령의 종료 상태를 얻을 수 있습니다.

unset -v false_status echo_status
{ code=$(
    exec 3>&1 >&4 4>&-
    cat 3>&- <(false 3>&-; echo >&3 "false_status=$?") \
             <(echo ok 3>&-; echo >&3 "echo_status=$?")
);} 4>&1
cat_status=$?
eval "$code"
printf '%s_code=%d\n' cat   "$cat_status" \
                      false "$false_status" \
                      echo  "$echo_status"

다음을 제공합니다.

ok
cat_code=0
false_code=1
echo_code=0

또는 pipefail지원하지 않는 쉘에서와 같이 수동으로 프로세스 대체를 사용 하고 구현할 수 있습니다 .

set -o pipefail
{
  false <&5 5<&- | {
    echo OK <&5 5<&- | {
      cat /dev/fd/3 /dev/fd/4
    } 4<&0 <&5 5<&-
  } 3<&0
} 5<&0