Bash에서 SIGPIPE를 억제하는 방법은 무엇입니까?

Bash에서 SIGPIPE를 억제하는 방법은 무엇입니까?

다음 코드를 실행하려고 합니다.

set -euxo pipefail
yes phrase | make installer

Makefile표준 입력을 사용하여 설치 phrase프로그램 파일을 만듭니다. 그러나 이 명령은 CI 빌드를 중단시키는 오류 코드 141로 끝납니다. 이 예는 다음과 같이 단순화될 수 있습니다.

yes | tee >(echo yo)

여기에서 볼 수 있습니다:출력을 티로 배관할 때 파이프 실패(141) - 이유는 무엇입니까?- 이 오류는 파이프 소비자가 출력 소비를 중단했음을 의미합니다. 제 경우에는 전혀 문제가 없습니다.

파이프 오류를 억제하고 그로부터 반환 코드만 가져오는 방법이 있습니까 make installer?

답변1

141 종료 코드는 프로세스가 실패했음을 나타내며 SIGPIPE이는 yes파이프가 닫힐 때 발생합니다. CI에 대해 이 오류를 마스킹하려면 다음과 같은 것을 사용하여 오류를 마스킹해야 합니다.

(yes phrase ||:) | make installer

그러면 실행되고 yes phrase, 실패하면 :코드 0으로 실행하고 종료합니다. yes쓸 수 없는 것 외에는 실패할 이유가 별로 없기 때문에 이 방법은 충분히 안전합니다 .

이러한 유형의 파이프라인 문제를 디버깅하는 가장 좋은 방법은 다음을 살펴보는 것입니다 PIPESTATUS.

yes phrase | make installer || echo "${PIPESTATUS[@]}"

그러면 실패 시 파이프라인의 모든 부분에 대한 종료 코드가 표시됩니다. 종료 코드 141로 인해 실패하는 경우는 적절하게 처리될 수 있습니다. 특정 오류 코드에 대한 일반적인 처리 패턴은 다음과 같습니다.

(command; ec=$?; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

(감사해요하우크라그); command성공하면 코드 0으로 실행되고 종료되고 command, 성공하면 코드 141로 종료됩니다. 다른 종료 코드는 그대로 반영됩니다.

답변2

파이프 오류를 억제하고 그로부터 반환 코드만 가져오는 방법이 있습니까 make installer?

하위 쉘에서 전체 명령 파이프라인을 실행하고 싶지 않은 경우(예: 변수를 설정할 수 있기를 원하는 경우) 게시된 다른 훌륭한 답변에 대한 대체 솔루션을 사용할 수 있습니다.

yes phrase | make installer || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

# generic syntax:
cmd1 | cmd2 | cmd3 || { ec=$?; [ $ec -eq 141 ] && true || (exit $ec); }

이는 exit하위 셸의 명령을 사용하여 명령 파이프라인에 원래 종료 코드를 그대로 둡니다(141이 아닌 경우). 따라서 set -e( set -o errexit)가 와 함께 작동하면 원하는 효과를 얻을 수 있습니다 set -o pipefail.

return대신 서브쉘 트릭에 사용할 수 있는 함수를 사용하면 더 깔끔한 코드를 얻을 수 있습니다 .exit

handle_pipefails() { 
    # ignore exit code 141 from simple command pipes
    # - use with: cmd1 | cmd2 || handle_pipefails $?
    (( $1 == 141 )) && return 0
    return $1
}

# then use it or test it as:
yes | head -n 1 || handle_pipefails $?
echo "ec=$?"

# then change the tested code from 141 to e.g. 999 in
# the function, and see that ec was in fact captured as
# 141

더 복잡한 파이프라인과 관련된 다른 명령의 종료 코드를 테스트하려는 경우 또 다른 접근 방식은 전체 PIPESTATUS를 테스트하는 것입니다.

handle_pipefails2() {
    # ignore exit code 141 from more complex command pipes
    # - use with: cmd1 | cmd2 | cmd3 || handle_pipefails2 "${PIPESTATUS[@]}"
    for x in "$@"; do
        (( $x == 141 )) || { (( $x > 0 )) && return $x; }
    done
    return 0
}

답변3

( yes phrase ; exit 0 ) | make installer

답변4

약간의 조사 끝에 다음 답변을 찾았습니다.https://stackoverflow.com/questions/22464786/ignoring-bash-pipefail-for-error-code-141#comment60412687_33026977 기본적으로 다음을 사용하십시오.

set -euxo pipefail
yes phrase | make installer || (ec=$? ; if [ "$ec" -eq 141 ]; then exit 0; else exit "$ec"; fi)

SIGPIPE반환 코드에서 필터링되었습니다.

관련 정보