stdout이 아닌 stderr를 파이프하는 방법은 무엇입니까?


981

나는에 정보를 기록하는 프로그램이 stdout과를 stderr, 그리고에 내가 필요로 grep무엇에오고 통해 표준 에러 무시하면서, 표준 출력을 .

물론 2 단계로 수행 할 수 있습니다.

command > /dev/null 2> temp.file
grep 'something' temp.file

하지만 임시 파일 없이이 작업을 수행하는 것을 선호합니다. 똑똑한 파이핑 트릭이 있습니까?


비슷한 질문이지만 stdout 유지 : unix.stackexchange.com/questions/3514/…
joeytwiddle

이 질문은 Bash에 관한 것이지만 Bourne / Almquist 쉘에 대한 관련 기사 를 언급 할 가치가 있습니다.
Stephen Niedzielski

10
나는 다음과 같은 것을 기대하고 있었다 command 2| othercommand. Bash는 매우 완벽하여 1982 년에 개발이 끝났으므로 Bash에서는이를 볼 수 없습니다.
Rolf

답변:


1189

먼저 stderr을 stdout (파이프)으로 리디렉션합니다. 그런 다음 stdout을 /dev/null(stderr의 위치를 ​​변경하지 않고) 리디렉션하십시오 .

command 2>&1 >/dev/null | grep 'something'

모든 종류의 I / O 리디렉션에 대한 자세한 내용은 Bash 참조 매뉴얼의 리디렉션 에 대한 장을 참조하십시오 .

I / O 리디렉션 시퀀스는 왼쪽에서 오른쪽으로 해석되지만 파이프는 I / O 리디렉션이 해석되기 전에 설정됩니다. 1 및 2와 같은 파일 디스크립터는 열린 파일 설명을 참조합니다. 이 조작 2>&1은 파일 디스크립터 2 (일명 stderr)가 파일 디스크립터 1 (일명 stdout이 현재 참조 dup2()하고 있음 open()) 과 동일한 열린 파일 설명을 참조하게합니다 . 동작은 >/dev/null다음을위한 오픈 파일 설명을 참조하도록 파일 기술자 1을 변경 /dev/null,하지만 파일 기술자 (2)는 파일 기술자 1 원래 가리키는 된 열린 파일 설명을 참조한다는 사실은 변경되지 않습니다 - 즉, 파이프.


44
나는 다른 날에 / dev / stdout / dev / stderr / dev / stdin을 우연히 발견했으며, 같은 일을하는 좋은 방법인지 궁금합니다. 나는 항상 2> & 1이 약간 난독하다고 생각했다. 그래서 같은 : command 2> /dev/stdout 1> /dev/null | grep 'something'
마이크 리용

17
/dev/stdoutet al을 사용 하거나을 사용할 수 있습니다 /dev/fd/N. 쉘이 특수한 경우로 처리하지 않으면 효율성이 약간 떨어집니다. 순수한 숫자 표기법은 이름으로 파일에 액세스하는 것을 포함하지 않지만 장치를 사용한다는 것은 파일 이름 조회를 의미합니다. 당신이 그것을 측정 할 수 있는지 여부는 논쟁의 여지가 있습니다. 나는 숫자 표기법의 간결함을 좋아합니다. 그러나 나는 그것을 현대 세계에서 그 장점을 판단 할 자격이 없을 정도로 오랫동안 (1 세기 이상 동안) 사용했습니다.
Jonathan Leffler

23
@Jonathan Leffler : 'stderr를 stdout으로 리디렉션 한 다음 stdout을 / dev / null로 리디렉션'이라는 평범한 텍스트 설명에 약간의 문제가 있습니다. 왼쪽에서 오른쪽이 아닌 오른쪽에서 왼쪽으로 리디렉션 체인을 읽어야하므로 또한 일반 텍스트 설명을 'stdout을 / dev / null로 리디렉션 한 다음 stderr를 stdout이 있던 위치로 리디렉션'에 적용해야합니다 .
커트 파이 플

116
@KurtPfeifle : au 반대! 쉘이 그것들을 처리하는 방식이기 때문에 왼쪽에서 오른쪽으로 리디렉션 체인을 읽어야합니다. 첫 번째 작업은 2>&1'stdout이 현재 진행중인 파일 설명자에 stderr 연결'을 의미 합니다. 두 번째 작업은 'stdout 변경'이므로 /dev/nullstderr는 원래 stdout 인 파이프로 이동합니다. 셸은 파이프 기호에서 먼저 항목을 분할하므로 파이프 리디렉션은 2>&1또는 >/dev/null리디렉션 전에 발생 하지만 그게 전부입니다. 다른 작업은 왼쪽에서 오른쪽입니다. (오른쪽에서 왼쪽으로 작동하지 않습니다.)
Jonathan Leffler

14
이것에 대해 정말로 놀랍게도 Windows에서도 작동한다는 것입니다 (Windows로 이름 /dev/null을 바꾼 후 nul).
Michael Burr

364

또는 표준 오류의 출력과 표준 출력을 바꾸려면 다음을 사용하십시오.

command 3>&1 1>&2 2>&3

새 파일 디스크립터 (3)를 작성하여 1 (표준 출력)과 동일한 위치에 지정한 다음 fd 1 (표준 출력)을 fd 2 (표준 오류)와 동일한 위치에 지정하고 마지막으로 fd 2 (표준 오류)를 지정합니다 fd 3 (표준 출력)과 같은 곳에

표준 오류는 이제 표준 출력으로 제공되며 이전 표준 출력은 표준 오류로 유지됩니다. 이것은 과잉 일 수 있지만 Bash 파일 설명자에 대한 자세한 내용을 제공하기를 바랍니다 (각 프로세스마다 9 가지 사용 가능).


100
마지막 조정은 3>&-stdout에서 만든 여분의 설명자를 닫는 것입니다.
Jonathan Leffler

1
우리는 파일이 기술자 만들 수 stderr의 조합이 다른 stderr과를 stdout? 즉 stderr, 한 번에 두 개의 다른 파일로 이동할 수 있습니까?
스튜어트

다음은 여전히 ​​stdout에 오류를 인쇄합니다. 내가 무엇을 놓치고 있습니까? ls -l not_a_file 3> & 1 1> & 2 2> & 3> errors.txt
user48956

1
@ JonasDahlbæk : 조정은 주로 단정의 문제입니다. 실제로는 극심한 상황에서 EOF 감지 프로세스와 감지하지 않는 프로세스간에 차이가 생길 수 있지만 매우 독특한 상황이 필요합니다.
Jonathan Leffler

1
주의 : FD 3이 아직 사용 중이 아니며 닫지 않고 파일 설명자 1과 2의 스와핑을 실행 취소하지 않는다고 가정하므로 다른 명령으로 파이프 할 수는 없습니다. 자세한 내용과 해결 방법 은 이 답변 을 참조하십시오 . {ba, z} sh에 대한 훨씬 더 명확한 구문은 이 답변을 참조하십시오 .
Tom Hale

218

Bash에서는 프로세스 대체를 사용하여 서브 쉘로 리디렉션 할 수도 있습니다 .

command > >(stdlog pipe)  2> >(stderr pipe)

당면한 경우 :

command 2> >(grep 'something') >/dev/null

1
화면에 출력하는 데 매우 효과적입니다. grep 출력을 파일로 리디렉션하면 응답하지 않는 내용이 다시 나타나는 이유를 알고 있습니까? command 2> >(grep 'something' > grep.log)grep.log에 ungrepped.log와 동일한 출력이 포함 된 후command 2> ungrepped.log
Tim

9
사용하십시오 2> >(stderr pipe >&2). 그렇지 않으면 "stderr pipe"의 출력이 "stdlog pipe"를 통과합니다.
ceving

그래!, 2> >(...)작동, 나는 시도 2>&1 > >(...)했지만 그것은하지 않았다
datdinhquoc

다음에이 작업을 수행 할 때 도움이 될만한 작은 예가 있습니다. ... 다음 사항을 고려 awk -f /new_lines.awk <in-content.txt > out-content.txt 2> >(tee new_lines.log 1>&2 ) 내가하고 싶었이 경우 내 콘솔에서 오류로 나오고 있었는지를 참조하십시오. 그러나 STDOUT은 출력 파일로 가고있었습니다. 따라서 하위 셸 내부에서 해당 STDOUT을 괄호 안의 STDERR로 다시 리디렉션해야합니다. 작동하는 동안 tee명령 의 STDOUT 출력 은 out-content.txt파일 끝에서 감 깁니다 . 그것은 저에게 일관성이없는 것 같습니다.
것이다

@datdinhquoc 어떻게 든처럼 한2>&1 1> >(dest pipe)
알리레자 Mohamadi

195

다음과 같은 경우 최선의 답변을 결합하십시오.

command 2> >(grep -v something 1>&2)

... 그런 다음 모든 stdout은 stdout으로 유지 되고 모든 stderr은 stderr로 유지되지만 stderr에는 "something"이라는 문자열이 포함 된 줄이 표시되지 않습니다.

이것은 stdout과 stderr를 되돌 리거나 버리지 않고 함께 밀거나 임시 파일을 사용하지 않는 독특한 이점이 있습니다.


아닌가 command 2> >(grep -v something)(없이 1>&2) 같은?
Francesc Rosas

11
그렇지 않으면 필터링 된 stderr이 stdout으로 라우팅됩니다.
Pinko

1
이것은 내가 필요한 것입니다-tar는 디렉토리에 대해 "파일을 읽을 때 파일이 변경되었습니다"를 출력하므로 한 줄만 걸러 내고 다른 오류가 발생하는지 확인하십시오. 그래서 tar cfz my.tar.gz mydirectory/ 2> >(grep -v 'changed as we read it' 1>&2)작동합니다.
20:10에

"문자열로 시작"이라고 말하는 것은 잘못입니다. 주어진 문자열로 시작하는 행만 제외시키는 grep의 제시된 구문에 대해서는 아무것도 없습니다. 주어진 문자열이 어디에나 포함되어 있으면 라인이 제외됩니다.
Mike Nakis

@MikeNakis 감사합니다-수정되었습니다! (그것이 의미가있는 원래의 답안 초안에서 남은 것입니다 ...)
Pinko

102

"리디렉션"및 "파이프"로 실제로 진행되는 작업에 대해 생각하면 사물을 시각화하는 것이 훨씬 쉽습니다. bash의 경로 재 지정 및 파이프는 한 가지 작업을 수행합니다. 프로세스 파일 디스크립터 0, 1 및 2가 가리키는 위치를 수정하십시오 (/ proc / [pid] / fd / * 참조).

파이프 또는 "|" bash는 fifo를 생성하고 왼쪽 명령의 FD 1을이 fifo로 가리키고 오른쪽 명령의 FD 0을 동일한 fifo로 가리 킵니다.

다음으로 각 측면의 리디렉션 연산자 가 왼쪽에서 오른쪽으로 평가 되고 설명자 복제가 발생할 때마다 현재 설정이 사용됩니다. 파이프가 먼저 설정 되었기 때문에 FD1 (왼쪽)과 FD0 (오른쪽)이 이미 원래 있던 것에서 이미 변경되었으므로 이러한 중복이 해당 사실을 반영하기 때문에 중요합니다.

따라서 다음과 같은 내용을 입력하면

command 2>&1 >/dev/null | grep 'something'

다음은 순서대로 수행됩니다.

  1. 파이프 (fifo)가 생성됩니다. "명령 FD1"이이 파이프를 가리 킵니다. "grep FD0"도이 파이프를 가리 킵니다
  2. "명령 FD2"는 "명령 FD1"이 현재 가리키는 곳 (파이프)을 가리 킵니다.
  3. "명령 FD1"은 / dev / null을 가리 킵니다.

따라서 "command"가 FD 2 (stderr)에 쓰는 모든 출력은 파이프로 향하고 반대쪽의 "grep"에 의해 읽 힙니다. "command"가 FD 1 (stdout)에 쓰는 모든 출력은 / dev / null로 이동합니다.

대신 다음을 실행하십시오.

command >/dev/null 2>&1 | grep 'something'

다음과 같은 일이 발생합니다.

  1. 파이프가 작성되고 "명령 FD 1"및 "grep FD 0"이 지시됩니다.
  2. "명령 FD 1"은 / dev / null을 가리 킵니다.
  3. "명령 FD 2"는 FD 1이 현재 가리키는 위치를 가리 킵니다 (/ dev / null)

따라서 "command"의 모든 stdout 및 stderr은 / dev / null로 이동합니다. 파이프에 아무것도 들어 가지 않으므로 화면에 아무 것도 표시하지 않고 "grep"이 닫힙니다.

또한 리디렉션 (파일 설명자)은 읽기 전용 (<), 쓰기 전용 (>) 또는 읽기 / 쓰기 (<>) 일 수 있습니다.

마지막 메모. 프로그램이 FD1 또는 FD2에 무언가를 쓰는지 여부는 전적으로 프로그래머에게 달려 있습니다. 좋은 프로그래밍 방법은 오류 메시지가 FD 2로 가고 일반 출력이 FD 1로 가도록 지시하지만, 둘을 혼합하거나 규칙을 무시하는 조잡한 프로그래밍을 종종 보게 될 것입니다.


6
정말 좋은 답변입니다. 나의 첫번째 제안은 "fifo"의 첫 사용을 "fifo (명명 된 파이프)"로 바꾸는 것입니다. 나는 한동안 리눅스를 사용해 왔지만 어떻게 든 명명 된 파이프라는 또 다른 용어라는 것을 배우지 못했습니다. 이것은 나를 찾는 것에서 저를 구했을 것입니다. 그러나 다시는 내가 그것을 발견했을 때 본 다른 것들을 배우지 않았을 것입니다!
Mark Edington

3
@MarkEdington FIFO는 파이프 및 IPC와 관련 하여 명명 된 파이프의 다른 용어 일뿐 입니다. 보다 일반적인 맥락에서 FIFO는 선입 선출 (First in, first out)을 의미하며, 이는 큐 데이터 구조에서 삽입 및 제거를 설명합니다.
Loomchild

5
@Loomchild 물론입니다. 필자의 의견은 노련한 개발 자라하더라도 FIFO 가 명명 된 파이프 의 동의어 로 사용되는 것을 본 적이 없다는 것 입니다. 즉,이 몰랐 : en.wikipedia.org/wiki/FIFO_(computing_and_electronics)#Pipes이 -이 질문에 대해 나에게 시간을 저장 한 것이라고 명확히.
Mark Edington

39

Bash를 사용하는 경우 다음을 사용하십시오.

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines


4
아니, stdout과 stderr을 결합한 |&것과 같습니다 2>&1. 질문은 stdout 없이 출력 명시 적으로 요청했습니다 .
Profpatsch

3
∎ '| &'를 사용하면 command1의 표준 오류가 파이프를 통해 command2의 표준 입력에 연결됩니다. 2> & 1 |의 약어입니다.” 링크의 네 번째 단락에서 그대로 사용합니다.
Profpatsch

9
@ Profpatsch : Ken의 대답은 정확합니다 .stdout과 stderr을 결합하기 전에 stdout을 null로 리디렉션하므로 stdout이 이전에 / dev / null로 떨어졌기 때문에 stderr 만 파이프에 넣습니다.
Luciano

3
하지만 난 여전히 당신의 대답은, 잘못을 발견 >/dev/null |&으로 확장 >/dev/null 2>&1 | 수단 표준 출력 아이 노드는 아무도 때문에 파이프에 비어 (모두 연결 # 1 # 2는 / dev / null의 아이 노드) (예를 들어, 표준 출력의 아이 노드에 연결되어 ls -R /tmp/* >/dev/null 2>&1 | grep i빈 줄 것이지만 ls -R /tmp/* 2>&1 >/dev/null | grep i뜻을 #을 수 있습니다 stdout inode에 묶인 2가 파이프됩니다).
과일

3
켄 샤프, 나는 테스트하고 ( echo out; echo err >&2 ) >/dev/null |& grep "."출력을 제공하지 않습니다 (우리가 원하는 "err"). man bash말한다 | 경우 | 및 사용 ... 2 속기> & 1. 표준 오류를 표준 출력으로 암시 적으로 재지 정하는 것은 명령으로 지정된 재 지정 후에 수행됩니다. 먼저 명령의 FD1을 null로 리디렉션 한 다음 명령의 FD2를 FD1이 가리키는 위치로 리디렉션합니다. null이므로 grep의 FD0은 입력을받지 않습니다. 보다 자세한 설명 은 stackoverflow.com/a/18342079/69663 를 참조하십시오 .
unhammer

11

stdout 및 stderr을 파일로 영구적으로 리디렉션하려는 경우 stderr를 grep하고 stdout을 유지하여 tty에 메시지를 쓰십시오.

# save tty-stdout to fd 3
exec 3>&1
# switch stdout and stderr, grep (-v) stderr for nasty messages and append to files
exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out
# goes to the std.out
echo "my first message" >&1
# goes to the std.err
echo "a error message" >&2
# goes nowhere
echo "this nasty_msg won't appear anywhere" >&2
# goes to the tty
echo "a message on the terminal" >&3

6

이것은 command1 stdout을 그대로두고 command1 stderr을 command2 stdin으로 경로 재 지정합니다.

exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 3>&-

LDP 에서 가져온


2

방금 명명 된 파이프를 사용하여 stdout한 명령과 stderr다른 명령으로 보내는 솔루션을 만들었습니다.

간다

mkfifo stdout-target
mkfifo stderr-target
cat < stdout-target | command-for-stdout &
cat < stderr-target | command-for-stderr &
main-command 1>stdout-target 2>stderr-target

나중에 명명 된 파이프를 제거하는 것이 좋습니다.


0

rc 쉘을 사용할 수 있습니다 .

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

당신은 표준 출력과 파이프 표준 오류 폐기 할 방법이 예 그렙 의를 rc:

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

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

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

알다시피 파이프 뒤에 괄호를 사용하여 파이프 할 파일 설명자를 지정할 수 있습니다.

표준 파일 디스크립터는 다음과 같이 구성됩니다.

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

-3

따라 해보고, 잘 작동하는지 확인하십시오.

command > /dev/null 2>&1 | grep 'something'

작동하지 않습니다. 터미널에 stderr을 보냅니다. 파이프를 무시합니다.
Tripp Kinetics
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.