예를 들어 내 질문을 명확히 해야 합니다. 이 동작은 나에게 의미가 있습니다.
$ 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
있는 경로 이름 에 연결합니다. 언제command
예 cat
, 프로세스는 거기에 앉아서 EOF를 얻을 때까지 stdin에서 읽습니다. 이 경우 tee
표준 입력에서 읽은 모든 데이터는 문제 없이 파이프에 기록됩니다.
하지만 때command
예 echo 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 hi
hi
출력을 작성합니다 (|
파이프라인).tee
>(…)
에서 만든 다른 파이프 에서 표준 출력과 명령줄 인수를 읽고 씁니다 . 이로 인해 하나의 복사본이hi
터미널에 인쇄되고 하나의 복사본이>(…)
파이프의 버퍼에 저장됩니다.- 이전 지점과 평행하게
echo hi
종료하여 파이프의 쓰기 끝을 닫습니다|
. tee
입력 파일의 끝에 도달했음을 알 수 있습니다. 모든 데이터를 기록했으므로 종료됩니다.- Bash의 관점에서 보면 파이프의 양쪽이 모두 종료되었으므로 명령이 끝났습니다. 0이 반환 되므로
tee
파이프 상태는 0이다. - 1초 후에
sleep 1
종료되고echo yo
실행됩니다.
두 번째 변형에서는 시작하기 전에 강제로 종료하여 이전을 강제로 종료합니다 echo yo
. 이번에는 이벤트 순서는 다음과 같습니다.tee
tee
echo hi
,echo yo
세 개의 명령이sleep 1
병렬로 시작됩니다.echo hi
hi
출력을 작성합니다 (|
파이프라인).- 이전 글머리 기호와 평행하게
echo yo
인쇄yo
하고 종료합니다. - 잠시 후
sleep 1
종료하고tee
시작하십시오. tee
입력에서 읽고hi
이를 터미널(표준 출력) 및>(…)
인수로 전달된 파이프에 쓰려고 시도합니다. 읽기 위해 이 파이프를 연 프로세스(echo yo
)가 1초 전에 종료되었기 때문에 파이프에 쓰기 시도가 실패합니다.신호 파이프라인(신호 13, 여기서쉘은 128+signal_number를 보고합니다.).
G-Man이 설명하는 대로, hi
두 번째 경우에 표시되는지 여부는 tee
표준 출력 또는 파일 인수에 먼저 쓰려고 시도하는지 여부에 따라 달라집니다.
호출이 없으면 sleep
타이밍이 어느 방향으로든 진행될 수 있습니다(Linux에서는 약 절반/절반 정도를 얻습니다. 다른 커널은 하나의 타이밍을 다른 것보다 더 가능성 있게 만들 수 있습니다).