korn 쉘에서 반품 상태를 캡처하고 동시에 tee를 사용하는 방법은 무엇입니까? [복사]

korn 쉘에서 반품 상태를 캡처하고 동시에 tee를 사용하는 방법은 무엇입니까? [복사]

소스 코드를 고려해보세요:

1.부모.sh

#!/usr/bin/ksh
# No tee
ksh Child.sh;
exit_status=$?;
echo "Exit status: ${exit_status}"
# Using tee
ksh Child.sh | tee -a log.txt;
exit_status=$?;
echo "Exit status: ${exit_status}"

2.어린이.sh

#!/usr/bin/ksh
...
exit 1;

산출:

Exit status: 1
Exit status: 0

  • 변수는 $exit_statusChild.sh의 종료 상태를 캡처하므로 입니다 1.
  • 두 번째 경우에는 $exit_statustee의 종료 상태가 캡처됩니다 0.

그렇다면 종료 상태를 캡처하고 tee를 사용하는 방법은 무엇입니까?

답변1

재인쇄(및 개선)comp.unix.shell FAQ(왜냐면 제가 FAQ의 이 부분을 썼기 때문입니다):

cmd1|cmd2에서 cmd1의 종료 코드를 얻는 방법

먼저, cmd1 종료 코드가 0이 아닐 수 있지만 이는 오류를 의미하지 않습니다. 예를 들어, 이런 경우가 발생합니다.

cmd | head -n 1

141(또는 ksh93의 경우 269, yash의 경우 397) 종료 상태를 볼 수 있지만 cmd이는 한 줄을 읽은 후 종료하는 것이 cmdSIGPIPE 신호에 의해 중단되었기 때문입니다.head -n 1

파이프라인 요소의 종료 상태 이해

cmd1 | cmd2 | cmd3

zsh(및 Fish 3.1+) 사용:

종료 코드는 특수 배열로 제공됩니다 pipestatus. cmd1종료 코드는 in $pipestatus[1]이고 cmd3종료 코드는 in $pipestatus[3]이므로 $status/ 는 $?항상 와 동일합니다 $pipestatus[-1].

배쉬와 함께:

종료 코드는 특수 배열로 제공됩니다 PIPESTATUS. 종료 코드 는 에 있으므로 cmd1항상 (또는 4.2 이전 버전의 경우) 동일합니다 .${PIPESTATUS[0]}cmd3${PIPESTATUS[2]}$?${PIPESTATUS[-1]}${PIPESTATUS[@]: -1}

다른 Bourne형 쉘과 마찬가지로

종료 코드를 기본 셸에 전달하려면 트릭을 사용해야 합니다. 이는 파이프(2)를 사용하여 수행할 수 있습니다. 실행하는 대신 cmd1실행하여 cmd1; echo "$?"$?가 셸에 들어가는지 확인합니다.

exec 3>&1
code=`
  # now, inside the backticks, fd4 goes to the pipe
  # whose other end is read and stored in $code  for
  # later evaluation; fd1 is the normal standard output
  # preserved the line before with exec 3>&1

  exec 4>&1 >&3 3>&- 
  {
    cmd1 4>&-; echo "ec1=$?;" >&4
  } | {
    cmd2 4>&-; echo "ec2=$?;" >&4
  } | cmd3 4>&-
  echo "ec3=$?;" >&4
`
exec 3>&-
eval "$code"

$ec1, , $ec2의 종료 코드입니다 $ec3.

POSIX 쉘 사용

이 기능을 사용하면 작업을 더 쉽게 할 수 있습니다.

run() {
  j=1
  while eval "\${pipestatus_$j+:} false"; do
    unset "pipestatus_$j"
    j=$(($j+1))
  done
  j=1 com= k=1 l=
  for arg do
    case $arg in
      ('|')
        com="$com {
               $l "'3>&-
               echo "pipestatus_'$j'=$?" >&3
             } 4>&- |'
        j=$(($j+1)) l=;;
      (*)
        l="$l \"\${$k}\""
    esac
    k=$(($k+1))
  done
  com="$com $l"' 3>&- >&4 4>&-
       echo "pipestatus_'$j'=$?"'

  { eval "$(exec 3>&1; eval "$com")"; } 4>&1
  j=1
  ret=0
  while eval "\${pipestatus_$j+:} false"; do
    eval '[ "$pipestatus_'"$j"'" -eq 0 ] || ret=$pipestatus_'"$j"
    j=$(($j+1))
  done
  return "$ret"
}

다음과 같이 사용하세요:

run cmd1 \| cmd2 \| cmd3

종료 코드는 $pipestatus_1, $pipestatus_2및 에 있으며 가장 오른쪽의 0이 아닌 종료 상태 $pipestatus_3입니다 ( 일부 셸 옵션과 유사).$?pipefail

답변2

bash에서는 && 및 ||를 사용하여 이 작업을 수행할 수 있습니다. ksh에서도 비슷한 작업이 가능할 것 같습니다.

ksh Child.sh && exit_status="$?" || exit_status="$?" | tee -a log.txt;

편집하다:

@Stephane이 지적했듯이 A && B | CA는 stdout으로 출력되고 B만 C로 파이프됩니다. 출력을 그룹화하고 함께 파이핑해야 합니다. 서브셸에서 호출자에게 변수를 전달할 수는 없지만 다음과 같이 할 수 있습니다.

x=$(tempfile) && exit_status=$(ksh Child.sh > $x; echo $?) && (cat $x; rm $x) | tee -a log.txt

관련 정보