표준 오류 스트림을 잡는 방법 (stderr)?


76

오디오 클립의 메타 정보를 얻기 위해 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이 오류 스트림을 읽도록하려면 어떻게해야합니까?

nixCraft 링크를 사용하여 표준 오류 스트림을 표준 출력 스트림으로 리디렉션 한 다음 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
나는 grepstdout에서만 작동 할 수 있다고 생각합니다 (표준 소스를 찾을 수는 없지만). 즉, 모든 스트림을 stdout으로 먼저 변환해야합니다.
— Stefan Lasiewski

9
@Stefan : grepstdin에서만 작동 할 수 있습니다. grep의 stdin을 다른 명령의 stdout에 연결하는 쉘로 작성된 파이프입니다. 그리고 쉘은 stdout을 stdin에만 연결할 수 있습니다.
— Gilles

당신이 맞아요. 나는 그것이 정말로 말하려는 의도라고 생각합니다. 감사합니다 @ 길리스.
— Stefan Lasiewski

여전히 stdout을 인쇄 하시겠습니까?
— Mikel

답변:


52

bash익명 파이프를 사용 하지 않는 이유를 사용하는 경우 본질적으로 phunehehe가 말한 것에 대한 약어입니다.

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


3
+1 니스! 배쉬 만, 대안보다 깨끗합니다.
— Mikel

1
+1 cp출력 확인에 사용했습니다cp -r dir* to 2> >(grep -v "svn")
— Betlista

5
stderr에서 필터링 된 경로 재 지정된 출력을 다시 원하면 >&2, 예를 들어command 2> >(grep something >&2)
— tlo

2
이것이 어떻게 작동하는지 설명하십시오. 2>stderr를 파일로 리디렉션하면 알 수 있습니다. 이것은 file에서 읽습니다 >(grep -i Duration). 그러나 파일은 저장되지 않습니까? 이 기술은 무엇이며 이에 대해 더 많이 읽을 수 있습니까?
— Marko Avlijaš

1
파이프 (파일이 아님)를 작성하는 것은 "구문 설탕"이며 완료되면 해당 파이프를 제거하십시오. 그들은 파일 시스템에 이름이 주어지지 않기 때문에 효과적으로 익명입니다. Bash는이 프로세스 대체를 호출합니다.
— Jé Queue

48

일반적인 쉘 (zsh조차 포함)은 stdout에서 stdin까지의 파이프를 허용하지 않습니다. 그러나 모든 Bourne 스타일 쉘은 파일 디스크립터 재 할당 (에서와 같이 1>&2)을 지원합니다 . 따라서 일시적으로 stdout을 fd 3으로, stderr을 stdout으로 전환 한 다음 나중에 fd 3을 stdout에 다시 넣을 수 있습니다. 경우 stuff표준 출력에 약간의 출력과 표준 에러에 대한 몇 가지 출력을 생성하고, 적용 할 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

이 접근법은 효과가 있습니다. 불행히도, 제 경우에는 0이 아닌 반환 값이 반환되면 손실됩니다-반환 된 값은 0입니다. 항상 그런 것은 아니지만 현재보고있는 경우에 발생합니다. 저장할 방법이 있습니까?
— Faheem Mitha

2
@FaheemMitha 무엇을하고 있는지 잘 모르지만 pipestatus도움이 될 것입니다
— Gilles

1
@FaheemMitha 또한 set -o pipefail오류 상태로 수행하려는 작업에 따라 여기에서 도움이 될 수 있습니다. (예를 들어 set -e오류가 발생하지 않은 경우 set -o pipefail에도 원할 것입니다 .)
— Wildcard

@Wildcard 예, set -e오류가 발생했습니다.
— Faheem Mitha

rc쉘은 배관 열려진 수 있습니다. 아래 답변을 참조하십시오 .
— Rolf

20

이것은 phunehehe의 "temp file trick"과 유사하지만 대신 명명 된 파이프를 사용하여 결과를 출력 할 때 약간 더 가까운 결과를 얻을 수 있으므로 장기 실행 명령에 유용 할 수 있습니다.

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

이 구성에서 stderr은 "mypipe"라는 파이프로 연결됩니다. 때문에 grep파일 인수로 호출 된, 그것의 입력을 STDIN에 보이지 않는 것입니다. 불행히도, 일단 당신이 한 후에는 그 명명 된 파이프를 정리해야합니다.

Bash 4를 사용하는 경우에 대한 바로 가기 구문 command1 2>&1 | command2이 command1 |& command2있습니다. 그러나 이것은 순전히 구문 바로 가기라고 생각하며 STDERR을 STDOUT으로 리디렉션합니다.



1
이 |&구문은 팽창 된 Ruby stderr 스택 추적을 정리하는 데 완벽합니다. 너무 번거 로움없이 사람들을 마침내 붙잡을 수 있습니다.
— pgr

8

Gilles와 Stefan Lasiewski의 답변은 모두 좋지만이 방법은 더 간단합니다.

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

ffmpeg'sstdout을 인쇄 하고 싶지 않다고 가정합니다 .

작동 방식 :

  • 먼저 파이프
    • ffmpeg 및 grep이 시작되고 ffmpeg의 stdout이 grep의 stdin으로 이동
  • 다음에서 왼쪽에서 오른쪽으로 리디렉션
    • ffmpeg의 stderr는 stdout이 무엇이든 (현재 파이프) 설정됩니다.
    • ffmpeg의 stdout이 / dev / null로 설정되었습니다.

이 설명은 혼란 스럽습니다. 마지막 두 글 머리 기호는 "stderr을 stdout으로 리디렉션", "stdout (지금 stderr로)을 / dev / null로 리디렉션"으로 생각하게합니다. 그러나 이것이 실제로하는 일은 아닙니다. 그 진술은 반대 인 것 같습니다.
— Steve

1
@Steve 두 번째 글 머리표에는 "stderr 포함"이 없습니다. 당신은 본 적이 unix.stackexchange.com/questions/37660/order-of-redirections를 ?
— Mikel

아니, 그건 의미 내 당신이 영어로 설명하는 방법의 해석. 제공 한 링크는 매우 유용합니다.
— Steve

왜 / dev / null로 리디렉션해야합니까?
— Kanwaljeet Singh

7

이 테스트에 사용 된 스크립트는 아래를 참조하십시오.

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

일반적으로 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

(상기 명령 유의 다른 후 ./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

방향 전환을 전환하면 모든 중괄호가 필요하지 않습니다. 그냥하세요 ./stdout-stderr.sh 2>&1 >/dev/null | grep err.
— Mikel

3

을 사용하여 한 명령의 출력을 다른 명령으로 파이프하면 |표준 출력 만 리디렉션됩니다. 그 이유를 설명해야합니다

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

원하는 것을 출력하지 않습니다 (그러나 작동합니다).

오류 출력을 표준 출력으로 리디렉션하지 않으려면 오류 출력을 파일로 리디렉션 한 다음 나중에 grep 할 수 있습니다

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error

감사. it does work, though당신의 컴퓨터에서 작동한다는 의미입니까? 둘째, 파이프를 사용하여 지적했듯이 stdout 만 리디렉션 할 수 있습니다. stderr을 리디렉션 할 수있는 일부 명령 또는 bash 기능에 관심이 있습니다. (그러나 임시 파일 트릭은 아닙니다)
— Andrew-Dufresne

@Andrew 제 말은, 명령이 작동하도록 설계된 방식으로 작동한다는 것을 의미합니다. 그것은 당신이 원하는 방식으로 작동하지 않습니다 :)
— phunehehe

명령의 오류 출력을 다른 명령의 표준 입력으로 리디렉션 할 수있는 방법을 모르겠습니다. 누군가 지적 할 수 있다면 흥미로울 것입니다.
— phunehehe

2

스트림을 교환 할 수 있습니다. 이를 grep통해 원래 표준 오류 스트림으로 이동하면서 원래 터미널의 표준 출력으로 이동 한 출력을 얻을 수 있습니다.

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

이것은 먼저 출력을 위해 열린 새 파일 디스크립터 (3)를 작성하고 표준 오류 스트림 ( 3>&2)으로 설정하여 작동합니다 . 그런 다음 표준 오류를 표준 출력 ( 2>&1)으로 리디렉션합니다 . 마지막으로 표준 출력이 원래 표준 오류로 리디렉션되고 새 파일 설명자가 닫힙니다 ( 1>&3-).

귀하의 경우 :

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

그것을 테스트 :

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output

1

rc이 경우 쉘 을 사용하고 싶습니다 .

먼저 패키지를 설치하십시오 (1MB 미만).

다음은 버리고 grep하기 위해 stdout파이프 하는 방법의 예입니다 .stderrrc

find /proc/ >[1] /dev/null |[2] grep task

Bash를 떠나지 않고도 할 수 있습니다.

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

아시다시피, 구문은 간단하므로이 방법을 선호합니다.

파이프 문자 바로 뒤에 괄호 안에 파이프 할 파일 설명자를 지정할 수 있습니다.

표준 파일 디스크립터는 다음과 같이 번호가 매겨집니다 :

  • 0 : 표준 입력
  • 1 : 표준 출력
  • 2 : 표준 오차

0

bash 서브 프로세스 예제의 변형 :

두 줄을 stderr에 에코하고 st stderr를 파일에 에코하고 티를 grep하고 stdout에 다시 파이프

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

표준 출력 :

fff

some.load.errors (예 : stderr) :

asdf
fff

-1

이 명령을 시도

  • 임의의 이름으로 파일 만들기
  • 파일로 출력을 보내다
  • 파일 내용을 파이프로 보내기
  • grep

ceva-> 그냥 변수 이름 (영어 = 무엇인가)

ceva=$RANDOM$RANDOM$RANDOM; ffmpeg -i qwerty_112_0_0_record.flv 2>$ceva; cat $ceva | grep Duration; rm $ceva;

/tmp/$ceva현재 디렉토리를 임시 파일로 버리는 것보다을 사용 하고 싶습니다. 현재 디렉토리가 쓰기 가능하다고 가정합니다.
— Rolf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.