프로그램의 출력을 grep하고 출력을 정상적으로 에코하려면 어떻게 해야 합니까?

프로그램의 출력을 grep하고 출력을 정상적으로 에코하려면 어떻게 해야 합니까?

나는 뭔가 잘못되었을 때 오류 메시지를 인쇄하지만 그에 따라 종료 상태를 설정하지 않는 프로그램을 사용하고 있습니다. 종료 상태는 항상 0이며 성공을 나타냅니다. 쉘 스크립트에서 이 프로그램을 실행하고 오류 메시지가 발행되면 스크립트가 프로그램에 실패하도록 지시할 수 있도록 0이 아닌 종료 상태를 얻고 싶습니다.

오류 메시지는 일치 항목이 발견되었는지 여부에 따라 종료 상태를 일치시키고 설정할 수 있는 예측 가능한 패턴을 따르 grep므로 grep프로그램의 출력을 여기에 파이프하고 grep결과를 무효화하여 !원하는 종료 상태를 얻을 수 있습니다.

문제는 파이프로 연결하면 grep할 수 없다는 것입니다.바라보다프로그램의 출력은 grep소비되었기 때문에 더 이상 존재하지 않습니다. 어떻게든 출력에서 ​​오류 메시지를 검색하고 싶지만 오류 외에 다른 중요한 메시지가 있으므로 출력도 정상적으로 표시하고 싶습니다. 불행하게도 grep모든 입력, 일치하는 줄 및 일치하지 않는 줄을 전달하는 옵션은 없습니다.

내가 찾은 한 가지 방법최대작품은 다음과 같습니다

! my_program | tee /dev/stderr | grep -q "error message"

이는 프로그램의 출력을 stderr에 넣지 grep만 stderr에도 복사하므로 내가 볼 수 있도록 리디렉션되지 않습니다. 내 스크립트의 stdout과 stderr이 모두 터미널로 전송되면 괜찮습니다(따라서 어느 쪽이 메시지를 받는지는 중요하지 않습니다). 그러나 stdout과 stderr이 다른 위치로 리디렉션되면 메시지가 종료될 수 있습니다. 잘못된 위치.

POSIX 셸 및 GNU 도구를 사용하여 프로그램의 표준 출력 및/또는 표준 오류 스트림을 스캔 하고 그에 따라 종료 상태를 설정하는 동시에 두 스트림이 모두 일반 대상에 도달하도록 하는 bash(간결한) 방법이 있습니까 ?grep

( my_programstderr가 아닌 stdout에 오류 메시지를 기록하므로 stdout 스트림만 스캔할 수 있는 솔루션은 이상적이지는 않지만 괜찮습니다. 차이가 있다면 CentOS 7 작동에서 이 작업을 수행하고 있습니다.)

답변1

...grep과 같은 도구를 사용하여 프로그램의 stdout 및/또는 stderr 스트림을 스캔하고 이에 따라 종료 상태를 설정하는 동시에 두 스트림이 일반 대상에 도달하도록 하는 (간결한) 방법이 있습니까?

...my_program은 표준 오류가 아닌 표준 출력에 오류 메시지를 기록합니다.표준 출력 스트림만 스캔하는 솔루션은 괜찮을 것입니다.하지만 이상적이지는 않습니다.

내 솔루션이 대답했습니다.용감한위 부분입니다.

가장 쉬운 방법은 다음을 통한 것이라고 생각합니다 awk.

myprogram |
awk 'BEGIN {status = 0} /error message/ {status = 1} 1; END {exit(status)}'

awk명령은 입력한 모든 것을 있는 그대로 출력하지만, 결국 종료되는 상태는 "오류 메시지"가 입력의 일부인지 여부에 따라 달라집니다.

간결한 버전(더 짧은 변수 이름):

myprogram | awk 'BEGIN{s=0} /error message/{s=1} 1; END{exit(s)}'

답변2

$MSG다음은 stdout/stderr에서 오류 메시지를 grep 하고 오류 메시지가 나타나면 프로그램을 중지/중지하지 않는 몇 가지 라이너입니다 .

# grep on stdout, do not stop early
my_program | awk -v s="$MSG" '$0~s{r=1} 1; END{exit(r)}'

# grep on stdout, do stop early
my_program | awk -v s="$MSG" '$0~s{exit(1)} 1'

# grep on stderr, do not stop early
{ my_program 2>&1 >&3 | awk -v s="$MSG" '$0~s{r=1} 1; END{exit(r)}' >&2; } 3>&1

# grep on stderr, do stop early
{ my_program 2>&1 >&3 | awk -v s="$MSG" '$0~s{exit(1)} 1' >&2; } 3>&1

노트:

  • 모든 사람에게 해당: 앱의 스트림은 들어오는 것을 확인하면서 grep기본 tty 상태를 잃게 됩니다 . 이는 스트림에 기록되는 방식에 영향을 미칠 수 있습니다. 예를 들어 커서 위치를 제어할 수 없다고 가정할 수 있으므로 회전하는 진행률 표시줄이 인쇄되지 않을 수 있습니다.my_programawkmy_program

  • 모두에 대해: stdout과 stderr은 병합되지 않으며 평소와 같이 서로 독립적으로 리디렉션될 수 있습니다.

  • 모든 사람에게: 원래 종료 코드 my_program는 완전히 무시됩니다. 다른 간단한 옵션은 다음과 같습니다.my_program오류나 오류 메시지로 종료하는 경우 오류로 종료합니다.. 이 동작을 얻으려면 pipefail파이프라인을 실행하는 Bash 셸에서 이를 활성화 해야 합니다 . 예를 들어:

    (set -o pipefail; my_program | awk -v s="$MSG" '$0~s{exit(1)} 1')
    
  • stderr에 대한 grep 작업의 경우: 위의 간단한 명령줄에서는 파일 설명자 3이 사용되지 않는다고 가정합니다. 이는 일반적으로 할 수 있는 가정입니다. 이러한 가정을 피하기 위해 Bash가 사용되지 않도록 보장되는 파일 설명자를 할당하도록 할 수 있습니다.

    bash -c 'exec {fd}>&1; { my_program 2>&1 >&${fd} | awk -v s="$MSG" '\''$0~s{exit(1)} 1'\'' >&2; } ${fd}>&1'
    

답변3

두 개의 터미널을 나란히 사용해 보세요. 첫 번째 셸 창에서:

tail -F /tmp/xyzzy

두 번째에서는 정확히 tmp 파일에 대해서만 수행하는 작업입니다.

my_program | tee /tmp/xyzzy | grep -q "error message"

그 순서대로 시작하세요.

임시 파일을 자주 삭제하거나 새 이름을 선택해야 하기 때문에 투박하지만 작동합니다.

나중에 추가하려면... 다음과 같이 시도해 보세요.

my_program | tee /tmp/xyzzy ; grep -q "error message" /tmp/xyzzy

시퀀스의 종료 상태는 grep의 종료 상태입니다. 이건 당신이 원하는 것과 반대예요, 한숨. 그러니 부정하세요.

my_program | tee /tmp/xyzzy ; ! grep -q "error message" /tmp/xyzzy

답변4

baz()출력하는 데모 기능 만들기"부자"도착하다표준 출력그리고"술집"도착하다표준 에러:

baz() { echo foo ; echo bar >& 2 ; }

간단한 경우에는 실행해 grep foo보세요.표준 출력, 및 grep bar정보표준 에러:

{ baz 2>&1 1>&3 | grep bar 1>&2 ; } 3>&1 | grep foo

동일한 내용이 사용되지만 tee출력은 grep전혀 사용되지 않은 것처럼 보입니다.

{ baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar ; } 3>&1 | \
{ tee /dev/stderr | grep -q foo ; } 2>&1

grep -q bar이제 "BEEP!"을 인쇄한 후 조건을 추가하세요.표준 에러만약에술집발견하다:

{ baz 2>&1 1>&3 | tee /dev/stderr | grep -q bar && echo "BEEP!" >&2 ; } 3>&1 | \
{ tee /dev/stderr | grep -q foo ; } 2>&1

마지막 두 줄의 출력은 다음과 같습니다.

foo
bar
BEEP!

관련 정보