종료 된 부속 명령으로 파이핑 할 때 왜 bash while 루프가 종료되지 않습니까?


12

아래 명령이 왜 종료되지 않습니까? 종료하지 않고 루프가 무한정 실행됩니다.

더 복잡한 설정을 사용하여이 동작을 발견했지만 가장 간단한 명령 형식은 다음과 같습니다.

종료하지 않습니다 :

while /usr/bin/true ; do echo "ok" | cat ; done | exit 1

위의 오타가 없습니다. 각 '|' 파이프입니다. 'exit 1'은 실행 및 종료 된 다른 프로세스를 나타냅니다.

"exit 1"로 인해 while 루프에서 SIGPIPE가 발생하고 (리더가없는 파이프에 쓰기) 루프가 끊어 질 것으로 예상합니다. 그러나 루프는 계속 실행됩니다.

왜 명령이 멈추지 않습니까?


zsh가 정상적으로 종료됩니다.
Braiam

답변:


13

구현의 선택 때문입니다.

Solaris에서 동일한 스크립트를 실행하면 ksh93다른 동작 이 생성됩니다.

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

문제를 일으키는 것은 내부 파이프 라인입니다. 루프가 없으면 루프가 셸 / OS에서 종료됩니다.

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat bash에서 SIGPIPE 신호를 받고 있지만 쉘은 어쨌든 루프를 반복하고 있습니다.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

배쉬 문서 상태 :

쉘 은 값을 리턴하기 전에 파이프 라인의 모든 명령 이 종료 되기를 기다립니다 .

Ksh 설명서 상태 :

마지막 명령을 제외한 각 명령은 별도의 프로세스로 실행됩니다. 쉘 은 마지막 명령 이 종료 되기를 기다립니다 .

POSIX 상태 :

파이프 라인이 백그라운드에 있지 않으면 (비동기 목록 참조) 셸 은 파이프 라인에 지정된 마지막 명령 이 완료 될 때까지 대기하고 모든 명령 이 완료 될 때까지 기다릴 수도 있습니다 .


문제를 일으키는 것은 내부 파이프 라인이 아니라 echoSIGPIPE 를 기본적으로 무시하는 것입니다. (실제 바이너리를 강제 로 사용하기 위해) env echo대신 대신 사용하여 문제를 재현 할 수도 있습니다. (또한 출력 비교 와 .)echoecho{ echo hi; echo $? >&2; } | exit 1{ env echo hi; echo $? >&2; } | exit 1
루카스 Werkmeister에게

1

이 문제로 몇 년 동안 버그가 발생했습니다. 올바른 방향으로 조금 이동해 준 질 리아 그레에게 감사드립니다.

내 리눅스 상자에서 질문을 조금만 쉬면 예상대로 종료됩니다.

while true ; do echo "ok"; done | head

그러나 파이프를 추가하면 예상대로 종료 되지 않습니다 .

while true ; do echo "ok" | cat; done | head

그것은 몇 년 동안 나를 좌절시켰다. jilliagre가 작성한 답변을 고려하여 다음과 같은 훌륭한 해결책을 찾았습니다.

while true ; do echo "ok" | cat || exit; done | head

QED ...

글쎄요 좀 더 복잡한 것이 있습니다.

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

이것은 제대로 작동하지 않습니다. 나는 || exit조기에 종료하는 방법을 알고 있지만 첫 번째 echo는 일치하지 않으므로 grep루프가 즉시 종료됩니다. 이 경우 실제로의 종료 상태에 관심이 없습니다 grep. 내 해결 방법은 다른을 추가하는 것 cat입니다. 여기에 "tens"라는 이름의 스크립트가 있습니다 :

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

로 실행되면 올바르게 종료됩니다 tens | head. 하나님 감사합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.