it-swarm-ko.tech

다른 프로세스로 파이프 된 프로세스의 종료 상태 가져 오기

파이프와 연결된 foobar의 두 가지 프로세스가 있습니다.

$ foo | bar

bar는 항상 0을 종료합니다. 종료 코드 foo에 관심이 있습니다. 그것을 얻을 수있는 방법이 있습니까?

314
Michael Mrozek

bash를 사용하는 경우 PIPESTATUS 배열 변수를 사용하여 파이프 라인의 각 요소의 종료 상태를 얻을 수 있습니다.

$ false | true
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0

zsh를 사용하는 경우 배열은 pipestatus (사건!)이며 배열 인덱스는 하나에서 시작합니다.

$ false | true
$ echo "${pipestatus[1]} ${pipestatus[2]}"
1 0

값을 잃지 않는 방식으로 함수 내에서 그것들을 결합하려면 :

$ false | true
$ retval_bash="${PIPESTATUS[0]}" retval_zsh="${pipestatus[1]}" retval_final=$?
$ echo $retval_bash $retval_zsh $retval_final
1 0

bash 또는 zsh에서 위를 실행하면 동일한 결과를 얻을 수 있습니다. retval_bashretval_zsh 중 하나만 설정됩니다. 다른 하나는 비어 있습니다. 이렇게하면 함수가 return $retval_bash $retval_zsh로 끝날 수 있습니다 (따옴표가 없습니다!).

277
camh

이 작업을 수행하는 일반적인 3 가지 방법이 있습니다.

파이프 페일

첫 번째 방법은 pipefail 옵션 (ksh, zsh 또는 bash)을 설정하는 것입니다. 이것은 가장 간단하며 기본적으로 종료 상태 $?를 마지막 프로그램의 종료 코드로 설정하여 0이 아닌 종료합니다 (또는 성공적으로 종료 된 경우 0).

$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1

$ PIPESTATUS

Bash에는 마지막 파이프 라인에있는 모든 프로그램의 종료 상태를 포함하는 $PIPESTATUS (zsh$pipestatus)라는 배열 변수도 있습니다.

$ true | true; echo "${PIPESTATUS[@]}"
0 0
$ false | true; echo "${PIPESTATUS[@]}"
1 0
$ false | true; echo "${PIPESTATUS[0]}"
1
$ true | false; echo "${PIPESTATUS[@]}"
0 1

세 번째 명령 예제를 사용하여 필요한 파이프 라인의 특정 값을 얻을 수 있습니다.

별도의 처형

이것은 가장 다루기 힘든 솔루션입니다. 각 명령을 개별적으로 실행하고 상태를 캡처하십시오.

$ OUTPUT="$(echo foo)"
$ STATUS_ECHO="$?"
$ printf '%s' "$OUTPUT" | grep -iq "bar"
$ STATUS_GREP="$?"
$ echo "$STATUS_ECHO $STATUS_GREP"
0 1
253
Patrick

이 솔루션은 bash 특정 기능이나 임시 파일을 사용하지 않고 작동합니다. 보너스 : 결국 종료 상태는 실제로 종료 상태이며 파일의 일부 문자열이 아닙니다.

상태:

someprog | filter

someprog의 종료 상태와 filter의 출력을 원합니다.

내 해결책은 다음과 같습니다.

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

이 구성의 결과는 구성의 stdout으로 filter에서 stdout이고 구성의 종료 상태로 someprog에서 종료 상태입니다.


이 구문은 서브 쉘 {...} 대신 간단한 명령 그룹화 (...)에서도 작동합니다. 서브 쉘은 성능 비용에 영향을 미치며 여기에는 필요하지 않습니다. 자세한 내용은 훌륭한 bash 설명서를 읽으십시오. https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; } } 4>&1

불행히도 bash 문법에는 중괄호에 공백과 세미콜론이 필요하므로 구문이 훨씬 넓어집니다.

이 텍스트의 나머지 부분에서는 서브 쉘 변형을 사용합니다.


someprogfilter :

someprog() {
  echo "line1"
  echo "line2"
  echo "line3"
  return 42
}

filter() {
  while read line; do
    echo "filtered $line"
  done
}

((((someprog; echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

echo $?

출력 예 :

filtered line1
filtered line2
filtered line3
42

참고 : 자식 프로세스는 부모로부터 열린 파일 설명자를 상속합니다. 이는 someprog가 열린 파일 설명자 3과 4를 상속 함을 의미합니다. someprog가 파일 설명자 3에 쓰면 종료 상태가됩니다. read은 (는) 한 번만 읽기 때문에 실제 종료 상태는 무시됩니다.

someprog가 파일 설명자 3 또는 4에 쓸 수 있다고 걱정되면 someprog를 호출하기 전에 파일 설명자를 닫는 것이 가장 좋습니다.

(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1

someprog 이전의 exec 3>&- 4>&-someprog를 실행하기 전에 파일 디스크립터를 닫으므로 someprog의 경우 해당 파일 디스크립터는 존재하지 않습니다.

다음과 같이 쓸 수도 있습니다 : someprog 3>&- 4>&-


구성에 대한 단계별 설명 :

( ( ( ( someprog;          #part6
        echo $? >&3        #part5
      ) | filter >&4       #part4
    ) 3>&1                 #part3
  ) | (read xs; exit $xs)  #part2
) 4>&1                     #part1

아래에서 위로 :

  1. 서브 쉘은 파일 디스크립터 4가 stdout으로 경로 재 지정되어 작성됩니다. 이것은 서브 쉘에서 파일 디스크립터 4에 인쇄 된 모든 것이 전체 구성의 표준 출력으로 끝나는 것을 의미합니다.
  2. 파이프가 생성되고 왼쪽 (#part3) 및 오른쪽 (#part2)의 명령이 실행됩니다. exit $xs 또한 파이프의 마지막 명령이며 stdin의 문자열은 전체 구문의 종료 상태가됩니다.
  3. 파일 디스크립터 3이 stdout으로 경로 재 지정된 서브 쉘이 작성됩니다. 이것은이 서브 쉘에서 파일 디스크립터 3에 인쇄 된 것은 #part2로 끝나고 전체 구성의 종료 상태가됨을 의미합니다.
  4. 파이프가 생성되고 왼쪽 (#part5#part6) 및 오른쪽 (filter >&4)의 명령이 실행됩니다. filter의 출력은 파일 디스크립터 4로 경로 재 지정됩니다. #part1에서 파일 디스크립터 4는 stdout으로 경로 재 지정되었습니다. 이는 filter의 출력이 전체 구성의 표준 출력임을 의미합니다.
  5. #part6의 종료 상태가 파일 설명자 3으로 인쇄됩니다. #part3에서 파일 설명자 3이 #part2로 리디렉션되었습니다. 이는 #part6의 종료 상태가 전체 구성의 최종 종료 상태가됨을 의미합니다.
  6. someprog이 실행됩니다. 종료 상태는 #part5에 있습니다. stdout은 #part4의 파이프에 의해 filter로 전달됩니다. filter의 출력은 #part4에 설명 된대로 stdout에 도달합니다.
58
lesmana

정확히 당신이 요구 한 것은 아니지만 사용할 수 있습니다.

#!/bin/bash -o pipefail

파이프가 0이 아닌 마지막 리턴을 리턴하도록합니다.

코딩이 약간 적을 수 있습니다

편집 : 예

[[email protected] ~]# false | true
[[email protected] ~]# echo $?
0
[[email protected] ~]# set -o pipefail
[[email protected] ~]# false | true
[[email protected] ~]# echo $?
1
37
Chris

가능한 경우 종료 코드를 foo에서 bar로 공급하는 것입니다. 예를 들어, foo가 숫자 만있는 행을 생성하지 않는다는 것을 알고 있으면 종료 코드를 입력 할 수 있습니다.

{ foo; echo "$?"; } | awk '!/[^0-9]/ {exit($0)} {…}'

또는 foo의 출력에 . 만있는 행이 포함되어 있지 않다는 것을 알고있는 경우 :

{ foo; echo .; echo "$?"; } | awk '/^\.$/ {getline; exit($0)} {…}'

bar가 마지막 행을 제외한 모든 행에서 작동하도록하고 마지막 행을 종료 코드로 전달할 수있는 방법이 있으면 항상 수행 할 수 있습니다.

bar가 출력이 필요없는 복잡한 파이프 라인 인 경우 다른 파일 설명자에 종료 코드를 인쇄하여 그 일부를 무시할 수 있습니다.

exit_codes=$({ { foo; echo foo:"$?" >&3; } |
               { bar >/dev/null; echo bar:"$?" >&3; }
             } 3>&1)

이 후 $exit_codes는 일반적으로 foo:X bar:Y이지만, 모든 입력을 읽기 전에 bar가 종료되거나 운이 좋지 않으면 bar:Y foo:X가 될 수 있습니다. 최대 512 바이트의 파이프에 대한 쓰기는 모든 유니스에서 원자 적이므로 태그 문자열이 507 바이트 미만인 경우 foo:$?bar:$? 부분이 혼합되지 않습니다.

bar의 출력을 캡처해야하는 경우 어려워집니다. 종료 코드 표시처럼 보이는 행을 포함하지 않도록 bar의 출력을 정렬하여 위의 기술을 결합 할 수 있지만 어리석게 나타납니다.

output=$(echo;
         { { foo; echo foo:"$?" >&3; } |
           { bar | sed 's/^/^/'; echo bar:"$?" >&3; }
         } 3>&1)
nl='
'
foo_exit_code=${output#*${nl}foo:}; foo_exit_code=${foo_exit_code%%$nl*}
bar_exit_code=${output#*${nl}bar:}; bar_exit_code=${bar_exit_code%%$nl*}
output=$(printf %s "$output" | sed -n 's/^\^//p')

물론 상태를 저장하는 임시 파일 사용 의 간단한 옵션이 있습니다. 단순하지만 프로덕션에서는 단순하지 않은 :

  • 여러 스크립트가 동시에 실행 중이거나 동일한 스크립트가 여러 곳에서이 방법을 사용하는 경우 다른 임시 파일 이름을 사용해야합니다.
  • 공유 디렉토리에 임시 파일을 안전하게 작성하는 것은 어렵습니다. 종종 /tmp는 스크립트가 파일을 쓸 수있는 유일한 장소입니다. POSIX는 아니지만 현재 모든 심각한 유니스에서 사용할 수있는 mktemp 를 사용하십시오.
foo_ret_file=$(mktemp -t)
{ foo; echo "$?" >"$foo_ret_file"; } | bar
bar_ret=$?
foo_ret=$(cat "$foo_ret_file"; rm -f "$foo_ret_file")

파이프 라인에서 시작 :

foo | bar | baz

POSIX Shell 만 사용하고 임시 파일을 사용하지 않는 일반적인 솔루션은 다음과 같습니다.

exec 4>&1
error_statuses="`((foo || echo "0:$?" >&3) |
        (bar || echo "1:$?" >&3) | 
        (baz || echo "2:$?" >&3)) 3>&1 >&4`"
exec 4>&-

$error_statuses에는 실패한 프로세스의 상태 코드가 임의의 순서로 포함되어 있으며, 어떤 명령이 각 상태를 생성했는지 알려주는 인덱스가 있습니다.

# if "bar" failed, output its status:
echo "$error_statuses" | grep '1:' | cut -d: -f2

# test if all commands succeeded:
test -z "$error_statuses"

# test if the last command succeeded:
! echo "$error_statuses" | grep '2:' >/dev/null

$error_statuses 내 테스트에서; 그들 없이는 grep 개행 문자가 공백으로 강제되기 때문에 구별 할 수 없습니다.

17
Jander

그래서 나는 lesmana와 같은 답변을 제공하고 싶었지만 내 것이 아마도 더 간단하고 약간 더 유익한 순수한 Bourne-Shell 솔루션이라고 생각합니다.

# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.

나는 이것이 내부에서 가장 잘 설명된다고 생각합니다. command1은 stdout (파일 설명자 1)에서 일반 출력을 실행하고 인쇄 한 다음 일단 완료되면 printf가 stdout에서 command1의 종료 코드를 실행하고 인쇄하지만 stdout은 파일 기술자 3.

Command1이 실행되는 동안 stdout은 command2로 파이프됩니다 (printf의 출력은 파이프가 읽는 1이 아니라 파일 디스크립터 3으로 보내므로 command2로 보내지 않습니다). 그런 다음 command2의 출력을 파일 디스크립터 4로 재지 정하여 파일 디스크립터 1을 유지합니다. 파일 디스크립터 1의 출력 파일을 파일 디스크립터로 다시 가져 오기 때문에 파일 디스크립터 1을 약간 나중에 비워야합니다. 1 – 이것이 명령 대체 (백틱)이며 캡처하고 변수에 배치되기 때문입니다.

마지막 마술은 첫 번째 exec 4>&1는 별도의 명령으로 수행 한 것입니다. 파일 설명자 4를 외부 셸의 stdout의 복사본으로 엽니 다. 명령 대체는 내부에있는 명령의 관점에서 표준에 기록 된 모든 것을 캡처하지만 명령 대체에 관한 한 command2의 출력은 파일 설명자 4로 이동하므로 명령 대체는이를 캡처하지 않습니다. 명령 대체에서 "아웃"되면 스크립트의 전체 파일 디스크립터 1로 계속 진행됩니다.

(exec 4>&1는 명령 대체 내에서 파일 디스크립터에 쓰려고 할 때 많은 공통 쉘이 선호하지 않기 때문에 별도의 명령이어야합니다.이 명령은 "외부"명령에서 사용됩니다. 따라서 가장 간단한 이식 방법입니다.)

명령의 출력이 서로 뛰어 넘는 것처럼 덜 기술적이고 더 유쾌한 방법으로 볼 수 있습니다 : command1이 command2로 파이프 된 다음 printf의 출력이 명령 2 위로 건너 뛰면 command2가 catch하지 않습니다. printf가 제 시간에 도달하여 변수로 끝나고 command2의 출력이 표준 출력에 기록되는 것과 같은 방식으로 진행되는 것처럼 command 2의 출력은 명령 대체로 건너 뛰고 빠져 나옵니다. 일반 파이프에서.

또한 내가 이해하는 것처럼 변수 할당, 명령 대체 및 복합 명령은 모두 내부 명령의 리턴 코드에 대해 투명하기 때문에 $?에는 파이프에 두 번째 명령의 리턴 코드가 여전히 포함됩니다. 따라서 command2의 반환 상태가 전파되어야합니다. 이것이 추가 기능을 정의 할 필요가 없기 때문에 이것이 lesmana가 제안한 것보다 다소 더 나은 해결책이라고 생각합니다.

Lesmana가 언급 한 경고에 따르면, command1은 어느 시점에서 파일 디스크립터 3 또는 4를 사용하여 종료 될 수 있으므로보다 강력 해집니다.

exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-

필자의 예제에서는 복합 명령을 사용하지만 하위 쉘 (( ) 대신 { } 사용)도 작동하지만 효율성이 떨어질 수 있습니다.

명령은 파일 디스크립터를 실행하는 프로세스에서 파일 디스크립터를 상속하므로 전체 두 번째 행은 파일 디스크립터 4를 상속하고 복합 명령 다음에 3>&1는 파일 디스크립터 3을 상속합니다. 따라서 4>&-는 내부 복합 명령이 파일 디스크립터 4를 상속하지 않도록하고 3>&-는 파일 디스크립터 3을 상속하지 않으므로 command1은보다 깨끗한 '환경'을 제공합니다. 내부 4>&-3>&- 옆으로 이동할 수도 있지만 가능한 범위를 제한하지 않는 이유는 무엇입니까?.

일이 얼마나 자주 파일 디스크립터 3과 4를 직접 사용하는지 잘 모르겠습니다. 대부분의 시간에 프로그램은 사용하지 않는 순간 파일 디스크립터를 리턴하는 syscall을 사용하지만 때로는 코드가 파일 디스크립터 3에 직접 작성한다고 생각합니다. 추측 (파일 디스크립터가 열려 있는지 확인하고 열려있는 경우 사용하거나 그렇지 않으면 다르게 동작하는 프로그램을 상상할 수 있습니다). 따라서 후자는 아마도 명심하고 일반적인 경우에 사용하는 것이 가장 좋습니다.

12
mtraceur

moreutils 패키지가 설치되어 있으면 mispipe 유틸리티를 사용하여 요청한 내용을 정확하게 수행 할 수 있습니다.

11
Emanuele Aina

위의 lesmana 솔루션은 대신 { .. }를 사용하여 중첩 된 하위 프로세스를 시작하는 오버 헤드없이 수행 할 수 있습니다 (이러한 그룹화 된 명령 형식은 항상 세미콜론으로 끝나야 함을 기억하십시오). 이 같은:

{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1

대시 버전 0.5.5 및 bash 버전 3.2.25 및 4.2.42로이 구성을 확인 했으므로 일부 쉘이 { .. } 그룹화를 지원하지 않더라도 POSIX 호환입니다.

7
pkeller

다음은 일반적인 솔루션 중 하나를 사용할 수없는 경우 @Patrik의 답변에 대한 애드온을 의미합니다.

이 답변은 다음을 가정합니다.

  • $PIPESTATUS 또는 set -o pipefail를 모르는 쉘이 있습니다.
  • 병렬 실행에 파이프를 사용하려고하므로 임시 파일이 없습니다.
  • 스크립트가 중단되면 갑작스런 정전으로 인해 추가적인 혼란을 피하고 싶지 않습니다.
  • 이 솔루션은 비교적 읽기 쉽고 읽기 편해야합니다.
  • 추가 서브 쉘을 도입하고 싶지 않습니다.
  • 기존 파일 디스크립터를 피들 링 할 수 없으므로 stdin/out/err를 건드리지 않아야합니다 (단, 새로운 파일 디스크립터를 일시적으로 도입 할 수는 있음)

추가 가정. 당신은 모든 것을 제거 할 수 있지만, 이것은 조리법을 너무 많이 방해하므로 여기에서는 다루지 않습니다.

  • PIPE의 모든 명령에 종료 코드 0이 있다는 것만 알면됩니다.
  • 추가 측 파대 정보가 필요하지 않습니다.
  • 쉘은 모든 파이프 명령이 돌아 오기를 기다립니다.

이전 : foo | bar | baz, 그러나 마지막 명령의 종료 코드 만 반환합니다 (baz)

구함 : 파이프의 명령 중 하나라도 실패한 경우 $?0 (true)가 아니어야합니다.

후:

TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"

{ foo || echo $? >&9; } |
{ bar || echo $? >&9; } |
{ baz || echo $? >&9; }
#wait
! read TMPRESULTS <&8
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"

# $? now is 0 only if all commands had exit code 0

설명 :

  • 임시 파일은 mktemp로 작성됩니다. 일반적으로 /tmp에 파일을 즉시 만듭니다.
  • 그런 다음이 임시 파일은 쓰기를 위해 FD 9로, 읽기를 위해 FD 8로 리디렉션됩니다.
  • 그런 다음 임시 파일이 즉시 삭제됩니다. 그러나 두 FD가 모두 사라질 때까지 열려 있습니다.
  • 이제 파이프가 시작되었습니다. 오류가있는 경우 각 단계는 FD 9에만 추가됩니다.
  • wait은 모든 파이프 명령이 완료되기를 기다리지 않기 때문에 ksh에는 ksh이 필요합니다. 그러나 일부 백그라운드 작업이 있으면 원하지 않는 부작용이 있으므로 기본적으로 주석 처리했습니다. 대기가 아프지 않으면 주석을 달 수 있습니다.
  • 그 후 파일 내용을 읽습니다. 비어 있으면 (모두 작동했기 때문에) readfalse를 반환하므로 true는 오류를 나타냅니다.

이것은 단일 명령의 플러그인 대체물로 사용될 수 있으며 다음과 같은 것만 필요합니다.

  • 사용하지 않은 FD 9 및 8
  • 임시 파일의 이름을 보유하는 단일 환경 변수
  • 그리고이 레시피는 IO 리디렉션을 허용하는) 어느 쉘에도 적용 할 수 있습니다.
  • 또한 플랫폼에 무관심하며 /proc/fd/N와 같은 것을 필요로하지 않습니다.

버그 :

/tmp에 공간이 부족한 경우이 스크립트에 버그가 있습니다. 이 인공 사례에 대한 보호가 필요한 경우 다음과 같이 수행 할 수 있지만 0000 수가 파이프의 명령 수에 따라 달라지는 단점이 있습니다. 따라서 약간 더 복잡합니다.

TMPRESULTS="`mktemp`"
{
rm -f "$TMPRESULTS"

{ foo; printf "%1s" "$?" >&9; } |
{ bar; printf "%1s" "$?" >&9; } |
{ baz; printf "%1s" "$?" >&9; }
#wait
read TMPRESULTS <&8
[ 000 = "$TMPRESULTS" ]
} 9>>"$TMPRESULTS" 8<"$TMPRESULTS"

휴대 성 노트 :

  • ksh 및 마지막 파이프 명령 만 기다리는 유사한 쉘에는 주석 처리되지 않은 wait가 필요합니다.

  • 마지막 예제는 이식성이 뛰어나므로 printf "%1s" "$?" 대신 echo -n "$?"를 사용합니다. 모든 플랫폼이 -n를 올바르게 해석하는 것은 아닙니다.

  • printf "$?"도 마찬가지이지만 printf "%1s"는 실제로 깨진 플랫폼에서 스크립트를 실행할 경우를 대비해 코너 케이스를 잡습니다. (읽기 : paranoia_mode=extreme로 프로그래밍하는 경우)

  • FD 8 및 FD 9는 여러 자리를 지원하는 플랫폼에서 더 높을 수 있습니다. POSIX 호환 쉘 이후에는 한 자리 만 지원하면됩니다.

  • 데비안 8.2 sh, bash, ksh, ash, sash 및 심지어 csh

5
Tino

이것은 이식성이 뛰어납니다. 즉, 모든 POSIX 호환 셸과 작동하며 현재 디렉토리를 쓸 수 있어야하며 동일한 트릭을 사용하여 여러 스크립트를 동시에 실행할 수 있습니다.

(foo;echo $?>/tmp/_$$)|(bar;exit $(cat /tmp/_$$;rm /tmp/_$$))

편집 : 여기 Gilles의 의견에 따른 강력한 버전이 있습니다.

(s=/tmp/.$$_$RANDOM;((foo;echo $?>$s)|(bar)); exit $(cat $s;rm $s))

Edit2 : 여기에 dubiousjim 주석에 따라 약간 가벼운 변형이 있습니다.

(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
4
jlliagre

약간의주의를 기울이면 다음과 같이 작동합니다.

foo-status=$(mktemp -t)
(foo; echo $? >$foo-status) | bar
foo_status=$(cat $foo-status)
3
alex

다음 'if'블록은 'command'가 성공한 경우에만 실행됩니다.

if command; then
   # ...
fi

구체적으로 말하면 다음과 같이 실행할 수 있습니다.

haconf_out=/path/to/some/temporary/file

if haconf -makerw > "$haconf_out" 2>&1; then
   grep -iq "Cluster already writable" "$haconf_out"
   # ...
fi

어느 것이 haconf -makerw하고 stdout 및 stderr을 "$ haconf_out"에 저장하십시오. haconf에서 반환 된 값이 true이면 'if'블록이 실행되고 grep은 "$ haconf_out"을 읽고 "Cluster already writable"과 일치 시키려고합니다.

파이프가 자동으로 청소됩니다. 리디렉션을 마치면 "$ haconf_out"을 제거 할 때주의해야합니다.

pipefail만큼 우아하지는 않지만이 기능이 도달 할 수없는 경우 합법적 인 대안입니다.

2
Rany Albeg Wein
Alternate example for @lesmana solution, possibly simplified.
Provides logging to file if desired.
=====
$ cat z.sh
TEE="cat"
#TEE="tee z.log"
#TEE="tee -a z.log"

exec 8>&- 9>&-
{
  {
    {
      { #BEGIN - add code below this line and before #END
./zz.sh
echo ${?} 1>&8  # use exactly 1x prior to #END
      #END
      } 2>&1 | ${TEE} 1>&9
    } 8>&1
  } | exit $(read; printf "${REPLY}")
} 9>&1

exit ${?}
$ cat zz.sh
echo "my script code..."
exit 42
$ ./z.sh; echo "status=${?}"
my script code...
status=42
$
1
C.G.

(적어도 bash로) set -e 서브 쉘을 사용하여 파이프 오류를 명시 적으로 에뮬레이트하고 파이프 오류시 종료

set -e
foo | bar
( exit ${PIPESTATUS[0]} )
rest of program

따라서 어떤 이유로 foo가 실패하면 나머지 프로그램은 실행되지 않고 해당 오류 코드와 함께 스크립트가 종료됩니다. (이것은 foo가 자체 오류를 인쇄한다고 가정합니다. 이는 실패 이유를 이해하기에 충분합니다)

0
noonex