'가 다른

프로세스가 두 개 있습니다. 및 bar :

$ foo | bar 

bar는 항상 0으로 나갑니다. foo의 종료 코드에 관심이 있습니다. 액세스 할 방법이 있습니까?

댓글

Answer

bashzsh에는 배열 변수가 있습니다. 쉘이 실행 한 마지막 파이프 라인의 각 요소 (명령)의 종료 상태를 보유합니다.

bash를 사용하는 경우 배열은 PIPESTATUS (대소 문자 중요!) 및 배열 색인은 0에서 시작합니다.

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

zsh, 배열은 pipestatus (대소 문자 중요!)라고하며 배열 색인은 1부터 시작합니다.

$ 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 

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

댓글

  • zsh의 pipestatus. 안타깝게도 다른 셸에는 ‘이 기능이 없습니다.
  • 참고 : zsh의 배열은 색인 1에서 반 직관적으로 시작하므로 ‘ s echo "$pipestatus[1]" "$pipestatus[2]".
  • 다음과 같이 전체 파이프 라인을 확인할 수 있습니다. if [ `echo "${PIPESTATUS[@]}" | tr -s ' ' + | bc` -ne 0 ]; then echo FAIL; fi
  • @JanHudec : 아마도 제 대답의 처음 다섯 단어를 읽어야 할 것 같습니다. 또한 질문에서 POSIX 전용 답변을 요청한 위치도 지적 해주세요.
  • @JanHudec : POSIX 태그도 없었습니다. 대답이 POSIX 여야하는 이유는 무엇입니까? 지정되지 않았으므로 자격있는 답변을 제공했습니다. 내 답변에는 잘못된 것이 없으며 다른 사례를 해결하기위한 여러 답변이 있습니다.

답변

있습니다. 이를 수행하는 3 가지 일반적인 방법은 다음과 같습니다.

Pipefail

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

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

$ PIPESTATUS

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 

댓글

  • Darn! PIPESTATUS에 대해 게시하려고했습니다.
  • 참고로이 SO 질문에서 논의 된 몇 가지 다른 기술이 있습니다. stackoverflow.com/questions/1221833/ …
  • @Patrick pipestatus 솔루션은 bash에서 작동합니다. ksh 스크립트를 사용하는 경우에 대비해 pipestatus와 유사한 것을 찾을 수 있다고 생각하십니까? , (ksh에서 지원하지 않는 pipestatus)
  • @yael ‘ ksh를 사용하지 않습니다. 하지만 간략히 살펴보면 ‘ 맨 페이지에서 ‘ $PIPESTATUS를 지원하지 않습니다. 또는 비슷한 것. 하지만 pipefail 옵션을 지원합니다.
  • 여기서 실패한 명령의 상태를 확인할 수 있으므로 pipefail을 사용하기로 결정했습니다. LOG=$(failed_command | successful_command)

Answer

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

상황 :

someprog | filter 

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

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

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

이 구조의 결과는 filter의 stdout이며 는 구성의 종료 상태입니다.


이 구조는 서브 쉘 (...) 대신 간단한 명령 그룹화 {...}에서도 작동합니다. 서브 쉘은 성능 비용과 관련하여 여기에서 필요하지 않습니다. 자세한 내용은 고급 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 

The someprogsomeprog를 실행하기 전에 파일 설명자를 닫기 전에 exec 3>&- 4>&-가 이러한 파일 설명자는 단순히 존재하지 않습니다.

다음과 같이 작성할 수도 있습니다. 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. A 파이프가 생성되고 왼쪽 (#part5#part6) 및 오른쪽 (filter >&4)가 실행됩니다. filter의 출력은 파일 설명자 4로 리디렉션됩니다. #part1에서 파일 설명자 4는 stdout으로 리디렉션되었습니다. 즉, filter의 출력은 전체 구성의 표준 출력입니다.
  5. #part6의 종료 상태가 인쇄됩니다. 파일 설명자 3. #part3에서 파일 설명자 3이 #part2로 리디렉션되었습니다. 이는 #part6의 종료 상태가 전체 구성의 최종 종료 상태가됨을 의미합니다.
  6. someprog는 실행. 종료 상태는 #part5에서 가져옵니다. 표준 출력은 #part4의 파이프에서 가져 와서 filter로 전달됩니다. filter의 출력은 #part4

댓글 h3에 설명 된대로 차례로 stdout에 도달합니다. >

  • 좋습니다. 함수의 경우 (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

Answer

정확히 요청한 것은 아니지만

#!/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에 없습니다.
  • 이것은 내 BASH 3.2.25 ( 1) 릴리스. / tmp / ff 상단에 #!/bin/bash -o pipefail가 있습니다.오류 : /bin/bash: line 0: /bin/bash: /tmp/ff: invalid option name
  • @FelipeAlvarez : 일부 환경 (Linux 포함)은 ‘

    줄이 첫 번째 줄 이후이므로 /bin/bash -o pipefail /tmp/ff, 필요한 /bin/bash -o pipefail /tmp/ffgetopt (또는 이와 유사한) 파싱은 optarg의 다음 항목 인 ARGV, -o에 대한 인수로 사용되므로 실패합니다. 방금 exec /bin/bash -o pipefail "$@"를 수행 한 래퍼 (예 : bash-pf)를 만들고 줄이면 작동합니다. 참조 : 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가 종료되면 bar:Y foo:X 일 수 있습니다. 모든 입력을 읽기 전에 또는 운이 좋지 않은 경우. 최대 512 바이트의 파이프에 대한 쓰기는 모든 유니스에서 원자 적이라고 생각하므로 foo:$?bar:$? 부분은 다음과 같이 혼합되지 않습니다. 태그 문자열이 507 바이트 미만이면됩니다.

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") 

설명

  • 임시 파일 접근 방식을 사용할 때 모든 임시 파일을 제거하는 EXIT에 대한 트랩을 추가하는 것을 선호합니다. 스크립트가 죽어도 쓰레기가 남지 않도록

답변

파이프 라인에서 시작 :

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 줄 바꿈이 공백으로 강제되기 때문에 구별 할 수 없습니다.

답변

moreutils 패키지가 설치되어있는 경우 요청한대로 정확히 수행하는 mispipe 유틸리티를 사용할 수 있습니다.

답변

그래서 저는 lesmana “s와 같은 답을 제공하고 싶었지만 제 생각에는 아마도 조금 더 간단하고 약간 더 유리한 순수- 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은 표준 출력 (파일 설명자 1)에서 일반 출력을 실행하고 인쇄합니다. , 일단 완료되면 printf는 stdout에서 command1의 종료 코드를 실행하고 인쇄하지만 해당 stdout은 파일 설명자 3으로 리디렉션됩니다.

command1이 실행되는 동안 stdout은 다음으로 파이프됩니다. command2 (printf의 출력은 1 대신 파일 설명자 3으로 보내기 때문에 command2로 전송되지 않습니다. s 파이프가 읽는 것).그런 다음 command2의 출력을 파일 설명자 4로 리디렉션하여 파일 설명자 1에서 벗어나도록합니다. 파일 설명자 1에서 파일 설명자 3의 printf 출력을 다시 파일 설명자 1 – “명령 대체 (백틱)”가 캡처하고 변수에 배치되는 것이기 때문입니다.

마지막 마법은 첫 번째 exec 4>&1 별도의 명령으로 수행했습니다. 파일 설명자 4를 외부 쉘의 표준 출력의 복사본으로 엽니 다. 명령 대체는 내부 명령의 관점에서 표준 출력에 기록 된 모든 내용을 캡처합니다. 그러나 command2 “의 출력은 명령 대체에 관한 한 파일 설명자 4로 이동하므로 명령 대체는이를 캡처하지 않습니다. 그러나 명령 대체가 “아웃”되면 여전히 효과적으로 스크립트의 전체 파일 설명자 1로 이동합니다.

(exec 4>&1는 대체 명령을 사용하는 “외부”명령에서 열리는 명령 대체 내부의 파일 설명자에 쓰려고 할 때 많은 공통 셸이이를 좋아하지 않기 때문에 별도의 명령이됩니다. 따라서 이것은 가장 간단한 이식 방법입니다. )

명령의 출력이 서로 도약하는 것처럼 덜 기술적이고 더 재미있는 방법으로 볼 수 있습니다. command1이 command2로 파이프되고 printf의 출력이 점프합니다. 명령 2를 넘겨서 command2가 “잡히지 않게하고 명령 2″의 출력이 넘어 갔다가 빠져 나옵니다. printf가 제 시간에 맞춰서 교체에 의해 캡처되어 변수로 끝나고 command2 “의 출력은 일반 파이프에서와 같이 표준 출력에 기록됩니다. / p>

또한 내가 알기로는 변수 할당, 명령 대체 및 복합 명령이 다음과 같기 때문에 $?에 두 번째 명령의 반환 코드가 파이프에 포함됩니다. 내부에있는 명령의 리턴 코드에 대해 모두 효과적으로 투명하므로 command2의 리턴 상태가 전파되어야합니다. 추가 기능을 정의 할 필요가 없기 때문에 이것이 제안 된 것보다 다소 더 나은 솔루션이라고 생각합니다. by lesmana.

lesmana가 언급 한주의 사항에 따라 command1이 언젠가는 파일 설명자 3 또는 4를 사용하게 될 가능성이 있으므로 더 강력하게하려면 다음을 수행하십시오.

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

예에서는 복합 명령을 사용하지만 하위 쉘 (iv { } 대신 id = “478eaad94a”>

도 작동하지만 효율성이 떨어질 수 있습니다.)

명령은 프로세스에서 파일 설명자를 상속합니다. 그러면 두 번째 줄 전체가 파일 설명자 4를 상속하고 3>&1 뒤에 오는 복합 명령은 파일 설명자 3을 상속합니다. 따라서 4>&-는 내부 복합 명령이 파일 설명자 4를 상속하지 않고 3>&-가 파일 설명자 3을 상속하지 않도록합니다. command1은 “더 깨끗하고”더 표준적인 환경을 제공합니다. 내부 4>&-3>&- 옆으로 이동할 수도 있지만 가능한 한 범위를 제한하지 않는 이유를 알아 봅니다. p>

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

댓글

  • 흥미롭게 보이지만 할 수 있습니다. ‘이 명령이 수행 할 작업을 예상하지 못하며 내 컴퓨터도 ‘ t 할 수 있습니다. -bash: 3: Bad file descriptor가 표시됩니다. li>
  • @ G-Man 맞아요, bash는 ‘가 공동으로 무엇을하는지 전혀 모릅니다. 내가 일반적으로 사용하는 쉘 (busybox와 함께 제공되는 ash)과는 달리 파일 설명자에 mes. ‘ bash를 행복하게 만드는 해결 방법을 생각하면 알려 드리겠습니다. 그동안 ‘ 데비안 상자가 있으면 대시 보드로 시도해 볼 수 있습니다. 또는 ‘ busybox가 있으면 편리합니다. busybox ash / sh로 시도해 볼 수 있습니다.
  • @ G-Man 명령이 수행 할 것으로 기대하는 작업과 다른 셸에서 수행하는 작업에 관해서는 command1에서 stdout을 리디렉션하므로 ‘ 명령 대체에 의해 잡히지 않지만 명령 대체를 벗어나면 fd3을 다시 표준 출력으로 떨어 뜨려 예상대로 ‘ 파이핑됩니다. command2에.command1이 종료되면 printf는 종료 상태를 실행하고 출력하며, 이는 명령 대체에 의해 변수에 캡처됩니다. 여기에 매우 자세한 분석이 있습니다. stackoverflow.com/questions/985876/tee-and-exit-status/ … 또한 , 당신의 의견은 모욕적 인 것처럼 읽혔습니까?
  • 어디부터 시작할까요? (1) 모욕을 받으 셨다면 죄송합니다. “재미있게 보입니다”는 진지하게 의미했습니다. 당신이 예상했던 것만 큼 잘 작동한다면 그것은 좋을 것입니다. 그 외에도 귀하의 솔루션이 무엇을해야하는지 이해하지 못했다는 것입니다. 나는 (리눅스가 존재하기 전부터) 오랜 동안 유닉스와 함께 일하고 있었는데, 뭔가 이해가되지 않는다면 그것은 아마도 나는> 다른 사람들도 그것을 이해하지 못할 것이며 더 많은 설명이 필요합니다 (IMNSHO). … (계속)
  • (계속)… 당신 “…… 생각하고 싶어요… 평범한 사람보다 더 많은 것”이라고 말하면 Stack Exchange의 목표는 사소하게 구별되는 질문에 대한 일회성 솔루션 수천 개를 만들어내는 명령 작성 서비스가 아니라는 점을 기억해야합니다. 오히려 사람들에게 자신의 문제를 해결하는 방법을 가르치는 것입니다. 그리고이를 위해 “일반인”이 이해할 수있을만큼 충분히 설명해야 할 수도 있습니다. 훌륭한 설명의 예는 lesmana의 답변을 참조하십시오. … (계속)

Answer

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

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

이 구문을 대시 버전 0.5.5 및 bash 버전 3.2.25 및 4.2.42로 확인 했으므로 일부 셸이 { .. } 그룹화는 여전히 POSIX를 준수합니다.

댓글

  • 이것은 대부분의 셸에서 정말 잘 작동합니다. I ‘ NetBSD sh, pdksh, mksh, dash, bash를 포함하여 사용해 보았지만 ‘ AT & T Ksh (93s +, 93u +) 또는 zsh (4.3.9, 5.2), ksh의 set -o pipefail 또는 임의의 수를 뿌린 경우에도 wait 명령 중 하나입니다. 부분적으로는 적어도 ksh에 대한 구문 분석 문제가 되십시오. 마치 내가 서브 쉘을 사용하는 것처럼 잘 작동하지만 if를 사용해도 ksh에 대한 서브 쉘 변형을 선택하지만 복합 명령은 그대로 둡니다. 다른 사람에게는 실패합니다.

Answer

다음은 @Patrik의 답변에 대한 추가 기능입니다. 일반적인 솔루션 중 하나를 사용할 수없는 경우

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

  • 또는 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에 추가됩니다.
  • ksh에는 wait가 필요합니다. ksh else는 모든 파이프 명령이 완료 될 때까지 기다리지 않기 때문입니다. 그러나 일부 백그라운드 작업이 있으면 원치 않는 부작용이 있으므로 기본적으로 주석 처리했습니다.기다려도 문제가되지 않으면 주석을 달 수 있습니다.
  • 이후 파일의 내용을 읽습니다. 비어있는 경우 (모두 작동했기 때문에) read false를 반환하므로 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 주석 처리가 필요 없습니다.

  • 마지막 예제는 echo -n "$?" 대신 printf "%1s" "$?"는 이동성이 더 높기 때문입니다. 모든 플랫폼이 -n를 올바르게 해석하는 것은 아닙니다.

  • printf "$?"도 해석합니다. 그러나 printf "%1s"는 실제로 손상된 플랫폼에서 스크립트를 실행하는 경우 일부 코너 케이스를 포착합니다. (읽기 : paranoia_mode=extreme에서 프로그래밍하는 경우.)

  • FD 8 및 FD 9는 다중 지원 플랫폼에서 더 높을 수 있습니다. 자릿수. AFAIR POSIX 준수 셸은 한 자리 숫자 만 지원하면됩니다.

  • Debian 8.2에서 테스트되었습니다. sh, bash, ksh, ash, sashcsh

답변

휴대용입니다. 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)) 

댓글

  • 몇 가지 이유로 작동하지 않습니다 ‘. 1. 임시 파일은 쓰기 전에 ‘ 읽을 수 있습니다. 2. 예측 가능한 이름으로 공유 디렉터리에 임시 파일을 만드는 것은 안전하지 않습니다 (사소한 DoS, 심볼릭 링크 경쟁). 3. 동일한 스크립트가이 트릭을 여러 번 사용하면 ‘ 항상 동일한 파일 이름을 사용합니다. 1을 해결하려면 파이프 라인이 완료된 후 파일을 읽으십시오. 2와 3을 해결하려면 임의로 생성 된 이름을 가진 임시 파일이나 개인 디렉터리를 사용합니다.
  • +1 음 $ {PIPESTATUS [0]}가 더 쉽지만 여기에있는 기본 아이디어는 다음과 같은 경우에 작동합니다. Gilles가 언급 한 문제에 대해 알고 있습니다.
  • 몇 가지 하위 셸을 저장할 수 있습니다. (s=/tmp/.$$_$RANDOM;{foo;echo $?>$s;}|bar; exit $(cat $s;rm $s)). @Johan : Bash를 사용하는 것이 ‘에 동의하지만 일부 상황에서는 Bash를 피하는 방법을 아는 것이 그만한 가치가 있습니다.

답변

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

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

댓글

  • jlliagre처럼 정리하는 것은 어떻습니까? ‘ foo-status라는 파일을 남겨 두지 않습니까?
  • @Johan : 제 제안을 선호한다면 ‘ 투표하는 것을 망설이지 말고;) 파일을 남기지 않는 것 외에도 여러 프로세스가이 작업을 동시에 실행할 수 있고 현재 디렉토리에 쓰기가 가능할 필요가 없다는 장점이 있습니다.

답변

다음 “if”블록은 “명령”이 성공한 경우에만 실행됩니다.

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 writeable”과 비교합니다.

파이프가 자동으로 정리됩니다. 리디렉션을 사용하면 “$ 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 $ 

답변

(적어도 bash 사용) set -e와 결합 된 하위 셸을 사용하여 pipefail을 명시 적으로 에뮬레이트하고 파이프 오류시 종료

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

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

Answer

수정 :이 답변은 틀렸지 만 흥미 롭기 때문에 나중에 남겨 두겠습니다. 참조.


명령에 !를 추가하면 반환 코드가 반전됩니다.

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. # =========================================================== # 

댓글

  • 관련이없는 것 같습니다. 귀하의 예에서는 bogus_command

의 종료 코드를 알고 싶습니다. / div>

  • 그 답을 되돌려 보겠습니다.
  • 글쎄요, 제가 ‘ 멍청한 것 같습니다. 저는 ‘이 작업이 OP가 원하는 것을했다고 생각하기 전에 실제로 스크립트에서 사용했습니다. 좋은 점은 ‘ 중요한 작업에는 사용하지 않았습니다. li>
  • 답글 남기기

    이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다