출력을 티로 배관할 때 파이프 실패(141) - 이유는 무엇입니까?

출력을 티로 배관할 때 파이프 실패(141) - 이유는 무엇입니까?

예를 들어 내 질문을 명확히 해야 합니다. 이 동작은 나에게 의미가 있습니다.

$ echo hi | cat
hi
$ echo hi | tee >(cat)
hi
hi

첫 번째 경우는 분명합니다. 두 번째 경우에는 명령 대체를 사용하여 "hi"를 tee에 파이프합니다. 하나의 "hi"는 tee'd로 인쇄되고 다른 "hi"는 cat' 전달 파이프로 인쇄됩니다. tee여태까지는 그런대로 잘됐다...

하지만 이 경우 첫 번째 "hi"는 어떻게 되나요?

$ echo hi | tee >(echo yo)
yo

반환 코드는 141이고 파이프라인이 실패했습니다. 원인은 무엇일까요?

저는 기본 터미널 애플리케이션에서 Mac OSX El Capitain, bash를 실행하고 있습니다.

답변1

나는 당신의 경험을 다른 사람들이 재현할 수 있는 것으로 조정하는 방법을 알아냈다고 생각합니다.

$ (에코헬로;슬립1;에코월드) 티셔츠 > (고양이)
안녕하세요
안녕하세요                                                       ...그리고 잠시 후,
세계
세계

$ 에코 "$?"
0

$ (에코헬로;슬립1;에코월드) 티셔츠 > (에코요) |
에야디야
안녕하세요

$ 에코 "$?"
141

여러분이 이해할 수 있듯이 실행 중인 프로세스에 대한 파이프를 생성합니다.>(command)command. 표준 입력은command명령줄(이 경우)의 다른 명령이 열고 쓸 수 tee있는 경로 이름 에 연결합니다. 언제commandcat, 프로세스는 거기에 앉아서 EOF를 얻을 때까지 stdin에서 읽습니다. 이 경우 tee표준 입력에서 읽은 모든 데이터는 문제 없이 파이프에 기록됩니다.

하지만 때commandecho yo, 프로세스는 yo표준 출력에 쓰고 즉시 종료됩니다. 이로 인해 문제가 발생합니다 tee. 다른 쪽 끝에 프로세스가 없는 파이프에 쓸 때 SIGPIPE 신호를 받습니다.

분명히 OS X 버전은 tee 먼저 명령줄에 파일을 쓴 다음 표준 출력에 파일을 씁니다. 따라서 귀하의 예 ( echo hi | tee >(echo yo)) 에서 tee파이프는 첫 번째 쓰기에서 실패합니다. 그러나 Linux 및 Cygwin 버전은 tee표준 출력에 기록합니다.첫 번째hi, 그래서 죽기 전에 화면에 쓸 수 있습니다 . 내 향상된 예에서는 파이프 tee에 쓸 때 hello죽어서 읽고 쓸 기회가 없습니다 world.

답변2

무슨 일이 일어나고 있는지 시각화하려면 다음 두 변형을 비교하십시오.

bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'

bash -c 'wait_and_tee () { sleep 1; tee "$@"; };
         echo hi | wait_and_tee >(echo yo); echo $?'

첫 번째 변종에서는 무슨 일이 일어났는지 주목하시나요?

$ bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'     
hi
0
$ yo

프로세스 대체 명령은 sleep 1; echo yo외부 명령과 병렬로 실행되며 bash는 완료될 때까지 기다리지 않습니다. 따라서 이벤트 순서는 다음과 같습니다.

  • echo hi, sleep 1; echo yo세 개의 명령이 tee병렬로 시작됩니다.
  • echo hihi출력을 작성합니다 ( |파이프라인).
  • tee>(…)에서 만든 다른 파이프 에서 표준 출력과 명령줄 인수를 읽고 씁니다 . 이로 인해 하나의 복사본이 hi터미널에 인쇄되고 하나의 복사본이 >(…)파이프의 버퍼에 저장됩니다.
  • 이전 지점과 평행하게 echo hi종료하여 파이프의 쓰기 끝을 닫습니다 |.
  • tee입력 파일의 끝에 도달했음을 알 수 있습니다. 모든 데이터를 기록했으므로 종료됩니다.
  • Bash의 관점에서 보면 파이프의 양쪽이 모두 종료되었으므로 명령이 끝났습니다. 0이 반환 되므로 tee파이프 상태는 0이다.
  • 1초 후에 sleep 1종료되고 echo yo실행됩니다.

두 번째 변형에서는 시작하기 전에 강제로 종료하여 이전을 강제로 종료합니다 echo yo. 이번에는 이벤트 순서는 다음과 같습니다.teetee

  • echo hi, echo yo세 개의 명령이 sleep 1병렬로 시작됩니다.
  • echo hihi출력을 작성합니다 ( |파이프라인).
  • 이전 글머리 기호와 평행하게 echo yo인쇄 yo하고 종료합니다.
  • 잠시 후 sleep 1종료하고 tee시작하십시오.
  • tee입력에서 읽고 hi이를 터미널(표준 출력) 및 >(…)인수로 전달된 파이프에 쓰려고 시도합니다. 읽기 위해 이 파이프를 연 프로세스( echo yo)가 1초 전에 종료되었기 때문에 파이프에 쓰기 시도가 실패합니다.신호 파이프라인(신호 13, 여기서쉘은 128+signal_number를 보고합니다.).

G-Man이 설명하는 대로, hi두 번째 경우에 표시되는지 여부는 tee표준 출력 또는 파일 인수에 먼저 쓰려고 시도하는지 여부에 따라 달라집니다.

호출이 없으면 sleep타이밍이 어느 방향으로든 진행될 수 있습니다(Linux에서는 약 절반/절반 정도를 얻습니다. 다른 커널은 하나의 타이밍을 다른 것보다 더 가능성 있게 만들 수 있습니다).

관련 정보