나는 두 개의 프로세스가 foo
와 bar
파이프 연결을 :
$ foo | bar
bar
항상 0을 종료합니다. 의 종료 코드에 관심이 foo
있습니다. 그것을 얻을 수있는 방법이 있습니까?
나는 두 개의 프로세스가 foo
와 bar
파이프 연결을 :
$ foo | bar
bar
항상 0을 종료합니다. 의 종료 코드에 관심이 foo
있습니다. 그것을 얻을 수있는 방법이 있습니까?
답변:
을 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_bash
와이 retval_zsh
설정됩니다. 다른 하나는 비어 있습니다. 이렇게하면 함수가 끝날 수 있습니다 return $retval_bash $retval_zsh
(따옴표가 없습니다!).
pipestatus
zsh에서. 불행히도 다른 쉘에는이 기능이 없습니다.
echo "$pipestatus[1]" "$pipestatus[2]"
.
if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
이 작업을 수행하는 일반적인 3 가지 방법이 있습니다.
첫 번째 방법은 pipefail
옵션 ( ksh
, zsh
또는 bash
) 을 설정하는 것 입니다. 이것은 가장 간단하며 기본적으로 종료 상태 $?
를 마지막 프로그램의 종료 코드로 설정하여 0이 아닌 종료합니다 (또는 모두 성공적으로 종료 된 경우 0).
$ false | true; echo $?
0
$ set -o pipefail
$ false | true; echo $?
1
Bash 에는 마지막 파이프 라인에있는 모든 프로그램의 종료 상태를 포함하는 $PIPESTATUS
( $pipestatus
in zsh
) 라는 배열 변수도 있습니다 .
$ 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
ksh
맨 페이지를 간략히 살펴보면 지원하지 않거나 $PIPESTATUS
비슷한 것이 없습니다 . pipefail
그래도 옵션을 지원합니다 .
LOG=$(failed_command | successful_command)
이 솔루션은 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 문법에는 중괄호에 공백과 세미콜론이 필요하므로 구문이 훨씬 넓어집니다.
이 텍스트의 나머지 부분에서는 서브 쉘 변형을 사용합니다.
예 someprog
및 filter
:
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를 상속 함을 의미 합니다. 파일 설명자 3에 someprog
쓰면 종료 상태가됩니다. read
한 번만 읽기 때문에 실제 종료 상태는 무시 됩니다.
someprog
파일 디스크립터 3 또는 4에 쓸 수 있다고 걱정되면을 호출하기 전에 파일 디스크립터를 닫는 것이 가장 좋습니다 someprog
.
(((((exec 3>&- 4>&-; someprog); echo $? >&3) | filter >&4) 3>&1) | (read xs; exit $xs)) 4>&1
는 exec 3>&- 4>&-
전에 someprog
실행하기 전에 파일 기술자를 닫 someprog
위해 이렇게 someprog
설명 단순히 존재하지 않는 파일입니다.
다음과 같이 쓸 수도 있습니다 : someprog 3>&- 4>&-
구성에 대한 단계별 설명 :
( ( ( ( someprog; #part6
echo $? >&3 #part5
) | filter >&4 #part4
) 3>&1 #part3
) | (read xs; exit $xs) #part2
) 4>&1 #part1
아래에서 위로 :
#part3
) 및 오른쪽 ( #part2
) 의 명령 이 실행됩니다. exit $xs
또한 파이프의 마지막 명령이며 stdin의 문자열이 전체 구문의 종료 상태가됨을 의미합니다.#part2
전체 구성의 종료 상태 가됨을 의미합니다 .#part5
및 #part6
) 및 오른쪽 ( filter >&4
) 의 명령 이 실행됩니다. 의 출력 filter
에서 설명 4. 파일로 재되는 #part1
파일 디스크립터 4 표준 출력으로 리디렉션한다. 이것은 출력이 filter
전체 구성의 표준 출력임을 의미합니다 .#part6
가 #part3
파일 설명자 3으로 인쇄됩니다. 파일 설명자 3이 (가)로 리디렉션되었습니다 #part2
. 이는 #part6
종료 상태가 전체 구성에 대한 최종 종료 상태가 됨을 의미합니다 .someprog
실행됩니다. 종료 상태는입니다 #part5
. stdout은 파이프로 가져와로 #part4
전달됩니다 filter
. 의 출력은 filter
차례로 설명 된대로 표준 출력에 도달합니다.#part4
(read; exit $REPLY)
(exec 3>&- 4>&-; someprog)
로 단순화합니다 someprog 3>&- 4>&-
.
{ { { { someprog 3>&- 4>&-; echo $? >&3; } | filter >&4; } 3>&1; } | { read xs; exit $xs; }; } 4>&1
정확히 당신이 요구 한 것은 아니지만 사용할 수 있습니다.
#!/bin/bash -o pipefail
파이프가 0이 아닌 마지막 리턴을 리턴하도록합니다.
코딩이 약간 적을 수 있습니다
편집 : 예
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
0
[root@localhost ~]# set -o pipefail
[root@localhost ~]# false | true
[root@localhost ~]# echo $?
1
set -o pipefail
예를 들어 누군가가를 통해 스크립트를 실행하는 경우 스크립트 내부가 더 강력해야합니다 bash foo.sh
.
-o pipefail
POSIX에 있지 않습니다.
#!/bin/bash -o pipefail
. 오류 :/bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
#!
은 첫 번째 환경을 넘어선 행을 공백으로 파싱하지 않으므로 , 다음 항목 인을 사용하여 /bin/bash
-o pipefail
/tmp/ff
필요한 /bin/bash
-o
pipefail
/tmp/ff
- getopt
(또는 비슷한) 파싱 대신에 - optarg
가 ARGV
됩니다. 에 -o
실패하므로 실패합니다. 래퍼를 만들어야하는 경우 (예 : bash-pf
방금 만든 exec /bin/bash -o pipefail "$@"
다음 #!
줄 에 넣으면 효과 가 있습니다.) 참조 : en.wikipedia.org/wiki/Shebang_%28Unix%29
가능한 경우 종료 코드를 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:Y foo:X
있으면 bar
모든 입력 읽기 전에 또는 당신이 운이 있다면 종료됩니다. 최대 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
스크립트가 파일을 작성할 수있는 유일한 곳입니다. mktemp
POSIX는 아니지만 현재 모든 심각한 유니스에서 사용할 수있는을 (를) 사용하십시오 .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 셸만 사용하고 임시 파일은 사용하지 않는 일반적인 솔루션입니다.
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
개행 문자가 공백으로 강제 변환되므로 구분할 수 없습니다.
그래서 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의 출력을 파일 디스크립터 3으로 다시 가져 오기 때문에 파일 디스크립터 1을 약간 나중에 비워야합니다. 1 – 명령 대체 (백틱)가 캡처하고 변수에 배치되기 때문입니다.
마지막 마술은 exec 4>&1
우리가 먼저 별도의 명령으로 수행 한 것입니다. 외부 디스크의 stdout의 복사본으로 파일 디스크립터 4를 엽니 다. 명령 대체는 내부에있는 명령의 관점에서 표준에 기록 된 내용을 캡처하지만 명령 대체에 관한 한 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에 직접 코드를 작성한다고 생각합니다. 추측 (파일 디스크립터가 열려 있는지 확인하고 열려있는 경우 사용하거나 그렇지 않으면 다르게 동작하는 프로그램을 상상할 수 있습니다). 따라서 후자는 아마도 명심하고 일반적인 경우에 사용하는 것이 가장 좋습니다.
-bash: 3: Bad file descriptor
.
당신이있는 경우 moreutils의 패키지를 설치하면 사용할 수 있습니다 mispipe의 당신이 질문을 정확하게 수행하는 유틸리티를.
위의 lesmana 솔루션은 { .. }
대신 중첩 하위 프로세스를 시작하는 오버 헤드없이 수행 할 수 있습니다 (이러한 그룹화 된 명령 형식은 항상 세미콜론으로 끝나야 함을 기억하십시오). 이 같은:
{ { { { someprog; echo $? >&3; } | filter >&4; } 3>&1; } | stdintoexitstatus; } 4>&1
대시 버전 0.5.5 및 bash 버전 3.2.25 및 4.2.42 로이 구성을 확인 했으므로 일부 쉘이 { .. }
그룹화를 지원하지 않더라도 여전히 POSIX 호환입니다.
set -o pipefail
ksh 또는 여러 가지 wait
명령을 사용 하더라도 AT & T Ksh (93s +, 93u +) 또는 zsh (4.3.9, 5.2)에서는 작동하지 않습니다. 서브 쉘 사용을 고집하는 것처럼 적어도 부분적으로 ksh의 구문 분석 문제 일 수 있지만 if
ksh의 서브 쉘 변형을 선택하지만 다른 명령의 복합 명령을 남겨두면 실패합니다. .
이식성이 뛰어납니다. 즉, 모든 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))
(s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s))
. @Johan : Bash를 사용하는 것이 더 쉽다는 데 동의하지만 일부 상황에서는 Bash를 피하는 방법을 아는 것이 좋습니다.
다음은 일반적인 솔루션 중 하나를 사용할 수없는 경우 @Patrik의 답변에 대한 애드온을 의미합니다.
이 답변은 다음을 가정합니다.
$PIPESTATUS
거나 껍질이set -o pipefail
추가 가정. 당신은 모든 것을 제거 할 수 있지만, 이것은 조리법을 너무 많이 방해하므로 여기에서는 다루지 않습니다.
- PIPE의 모든 명령에 종료 코드 0이 있다는 것만 알면됩니다.
- 추가 측 파대 정보가 필요하지 않습니다.
- 쉘은 모든 파이프 명령이 리턴되기를 기다립니다.
Before : foo | bar | baz
하지만 마지막 명령의 종료 코드 만 반환합니다 ( baz
).
구함 : 파이프의 명령 중 하나라도 실패한 경우 (참)이 $?
아니어야합니다 .0
후:
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
wait
위해 필요 ksh
하기 때문에, ksh
모든 파이프가 종료 명령에 대한 다른 기다리지 않습니다. 그러나 일부 백그라운드 작업이 있으면 원하지 않는 부작용이 있으므로 기본적으로 주석 처리했습니다. 대기가 아프지 않으면 주석을 달 수 있습니다.read
반환 false
하므로 true
오류를 나타냅니다.이것은 단일 명령의 플러그인 대체물로 사용될 수 있으며 다음과 같은 것만 필요합니다.
/proc/fd/N
버그 :
이 스크립트에는 /tmp
공간이 부족한 경우 버그가 있습니다. 이 인공 사례에 대한 보호가 필요한 경우 다음과 같이 수행 할 수 있지만 이는 파이프의 명령 수 0
에 000
따라의 수가 달라 지므로 약간 더 복잡하다는 단점이 있습니다.
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
다음 '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"에 저장합니다. from에서 반환 된 값 haconf
이 true이면 'if'블록이 실행되고 grep
"$ haconf_out"을 읽고 "Cluster already writable"과 일치 시키려고합니다.
파이프가 자동으로 청소됩니다. 리디렉션을 마치면 "$ haconf_out"을 제거 할 때주의해야합니다.
pipefail
이 기능이 도달 할 수없는 경우 우아 하지는 않지만 합법적 인 대안입니다.
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
$
편집 :이 답변은 잘못되었지만 흥미로워 나중에 참조 할 수 있도록 남겨 두겠습니다.
!
명령에
a 를 추가 하면 리턴 코드가 반전됩니다.
http://tldp.org/LDP/abs/html/exit-status.html
# =========================================================== #
# Preceding a _pipe_ with ! inverts the exit status returned.
ls | bogus_command # bash: bogus_command: command not found
echo $? # 127
! ls | bogus_command # bash: bogus_command: command not found
echo $? # 0
# Note that the ! does not change the execution of the pipe.
# Only the exit status changes.
# =========================================================== #
ls
반전시키지 마십시오bogus_command