Shell/Bash의 stderr에 인쇄된 항목에 대한 간단한 테스트가 있습니까?

Shell/Bash의 stderr에 인쇄된 항목에 대한 간단한 테스트가 있습니까?

지속적인 통합을 위해 쉘 스크립트에서 명령을 호출하고 싶습니다. 종료 상태 0은 성공을 나타내고, 그렇지 않으면 실패합니다. 여러 명령을 실행하고 명령 중 하나에 오류가 발생하면 실패하도록 래퍼 스크립트를 작성 중입니다.

그러나 명령 중 하나(타사 소프트웨어)는 실패 시 "사실상" 종료 상태 != 1을 따르지 않습니다. 그러나 실패가 발생하면 stderr에 오류를 인쇄합니다.

현재 래퍼 스크립트는 둘 다 제대로 작동하는 경우 스위치 로 mycommand인해 종료 상태로 인해 실패합니다 ! = 0:other-command-e

#!/bin/bash -ex
mycommand --some-argument /some/path/to/file
other-command --other-argument /some/other/file

stderr에 인쇄된 항목이 있는지 어떻게 확인할 수 있나요(주 스크립트 실패 원인)? 내가 시도한 것은 다음과 같습니다.

  1. stderr 출력이 파일로 리디렉션되고 파일 내용이 검사됩니다.
    임시 파일 생성을 피하고 싶습니다.
  2. 예를 들어, stderr를 서브쉘 stdin으로 리디렉션합니다.

    mycommand 2> >(if grep .; then echo NOK; else echo OK; fi)
    

    이것은 잘 작동하는 것 같지만 여기서는 메인 쉘 종료를 제어할 수 없습니다. 즉, exit 1메인 프로그램을 종료하지 않습니다. 또한 결과를 전파하기 위해 서브셸 외부의 변수를 제어할 수 없습니다. 정말 명명된 파이프 같은 것을 만들어야 합니까?

  3. 추가 파일 설명자를 설정합니다.이 답변.
    제가 보기엔 그다지 우아해 보이지는 않네요.

일부 "요구 사항":

  • stdout의 일반 출력(역시 출력)에서는 실패하지 않아야 합니다.
  • 표준 출력에 다른 유용한 출력을 유지하고 싶습니다.
  • 나는 현재 stderr에 인쇄된 모든 출력을 유지하고 싶습니다(stdout일 수 있지만 숨겨서는 안 됩니다).

따라서 래퍼처럼 동작해야 하며, 부정한 상태로만 종료하고 인쇄물을 유지해야 합니다.

stderr에서 무엇이든 확인할 수 있는 좀 더 우아한 기능이 있었으면 좋겠습니다. 모욕은 허용됩니다.

답변1

다음과 같이 할 수 있습니다(POSIXly).

if { cmd 2>&1 >&3 3>&- | grep '^' >&2; } 3>&1; then
  echo there was some output on stderr
fi

또는 원래 종료 상태를 유지합니다(0이 아닌 경우).

fail_if_stderr() (
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  err=$?
  [ "$rc" -eq 0 ] || exit "$rc"
  [ "$err" -ne 0 ] || exit 125
) 3>&1

125명령이 0 종료 상태를 반환하지만 일부 오류 출력을 생성하는 상황에는 종료 코드를 사용합니다.

다음과 같이 사용됩니다:

fail_if_stderr cmd its args || echo "Failed with $?"

답변2

# output "NOK" if standard error has any output; "OK" otherwise:
errlog=$(mktemp)
somecommand 1>> "$stdlog" 2> "$errlog"
if [[ -s "$errlog" ]]; then
    # File exists and has a size greater than zero
    echo "NOK"
else
    echo "OK"  
fi
# Done parsing standard error; tack it to the regular log
cat "$errlog" >> "$stdlog"
rm -f "$errlog"

답변3

가장 많은 표를 얻은 답변대부분의 경우에 작동합니다. 하지만 set +o errexitbash에서 사용하고 있기 때문에 오류가 발생합니다. 이것은 bash에서 더 잘 작동합니다.

fail_if_stderr() (
  # save current options
  bash_options="${-}"
  
  # disable exit on error
  set +o errexit
  
  # Save return code of command in rc
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  
  # Save return code of grep in err_in_stderr
  err_in_stderr=$?
  
  # enable exit on error if it was previously enabled
  test "${bash_options#*e*}" != "$bash_options" && set -o errexit
  
  # exit with original return code if it's not zero
  [ "$rc" -eq 0 ] || exit "$rc"
  
  # exit with return code 125 if something was in stderr
  [ "$err_in_stderr" -ne 0 ] || exit 125
) 3>&1

관련 정보