표준 오류 스트림(stderr)을 어떻게 grep합니까?

표준 오류 스트림(stderr)을 어떻게 grep합니까?

오디오 클립의 메타 정보를 얻기 위해 ffmpeg를 사용하고 있습니다. 하지만 나는 그것을 잡을 수 없습니다.

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --arch=i386 --extra-cflags=-O2 
      ...

확인해 보니 이 ffmpeg 출력이 stderr로 전달됩니다.

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

그래서 grep은 일치하는 라인을 잡기 위해 오류 스트림을 읽을 수 없다고 생각합니다. grep에서 오류 스트림을 읽으려면 어떻게 해야 합니까?

사용닉 크래프트링크를 통해 표준 오류 스트림을 표준 출력 스트림으로 리디렉션한 다음 grep이 작동했습니다.

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

하지만 stderr을 stdout으로 리디렉션하고 싶지 않다면 어떻게 해야 할까요?

답변1

bash익명 파이프를 사용하지 않는 이유는 기본적으로 phunehehe가 말한 내용을 축약한 것입니다 .

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

답변2

|모든 일반 쉘(zsh도 포함)은 stdout에서 stdin을 제외하고 연산자가 있는 파이프를 허용하지 않습니다. 그러나 모든 Bourne 스타일 쉘은 파일 설명자 재할당(예: 1>&2)을 지원합니다. 따라서 일시적으로 stdout을 fd 3으로 이동하고, stderr을 stdout으로 이동한 다음 fd 3을 다시 stdout에 넣을 수 있습니다. stuffstdout에서 일부 출력을 생성하고 stderr에서 일부 출력을 생성하고 표준 출력에 영향을 주지 않고 오류 출력에 적용하려는 경우 filter이를 사용할 수 있습니다 { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error

Ksh, bash 및 zsh는 임의의 파일 설명자에서 파이프를 지원하지만 다르게 작동합니다. 하나의 명령이 기본 명령이고 입력 또는 출력을 다른 명령으로 파이프할 수 있습니다.프로세스 대체기본 명령의 리디렉션에 사용할 수 있는 파이프에 연결된 표준 입력 또는 표준 출력을 사용하여 백그라운드에서 명령을 실행합니다. 여기서는 표준 오류를 필터링하려고 하므로 이를 출력 프로세스 대체로 리디렉션합니다.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ stuff 2> >(filter)
standard output
more output
standard error

>두 문자 사이에 공백이 있다는 점에 유의하십시오 . 이는 2>표준 출력 및 >(…)프로세스 대체를 리디렉션하기 위한 것입니다. 2>>…추가 리디렉션으로 해석되므로 공백이 필요합니다 .

답변3

이는 phunehehe의 "임시 파일 트릭"과 유사하지만 명명된 파이프를 사용하여 출력에 약간 더 가까워질 수 있으며 이는 장기 실행 명령에 매우 편리합니다.

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

이 구성에서 stderr는 "mypipe"라는 파이프로 연결됩니다. 파일 매개변수로 호출 되었으므로 grep입력을 얻기 위해 STDIN을 찾지 않습니다. 불행하게도 작업을 마친 후에도 명명된 파이프를 정리해야 합니다.

Bash 4를 사용하는 경우 단축 구문이 command1 2>&1 | command2있습니다 command1 |& command2. 그러나 나는 이것이 순전히 구문 지름길이라고 생각하며 여전히 STDERR을 STDOUT으로 리디렉션하고 있습니다.

답변4

이 테스트에 사용된 스크립트는 아래를 참조하세요.

Grep은 stdin에서만 작동할 수 있으므로 stderr 스트림을 Grep이 구문 분석할 수 있는 형식으로 변환해야 합니다.

일반적으로 stdout과 stderr이 모두 화면에 인쇄됩니다.

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

stdout을 숨기지만 여전히 stderr을 인쇄하려면 다음을 수행하십시오.

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

그러나 grep은 stderr에서 실행되지 않습니다! 다음 명령을 사용하면 "err"이 포함된 줄이 표시되지 않을 것으로 예상할 수 있지만 그렇지 않습니다.

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

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

다음 Bash 구문은 stdout에 대한 출력을 숨기지만 여전히 stderr을 표시합니다. 먼저 stdout을 /dev/null로 파이프한 다음 stderr를 stdout으로 변환합니다. 왜냐하면 Unix 파이프는 stdout에서만 작동할 수 있기 때문입니다. 여전히 텍스트를 찾을 수 있습니다.

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(위 명령은 다음과 같습니다.다른then 은 ./command >/dev/null 2>&1매우 일반적인 명령입니다).

테스트용 스크립트입니다. 그러면 stdout에 한 줄, stderr에 한 줄이 인쇄됩니다.

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0

관련 정보