표준 오류 스트림을 잡는 방법 (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 | command2command1 |& 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.