다른 프로세스에서 파이프를 통해 전송된 데이터를 처리하는 Bash 스크립트가 있습니다.
특정 조건이 충족된 후 발생하고 종료되는 함수를 추가하려고 하는데 다음과 같이 단순화되었습니다.
( while true; do date; sleep 1; done ) | ( for ((i = 0; i < 3; i++)); do read -r line; echo LINE: $line; sleep 1; done; exit 1 )
실제 사례:
- 파이프 뒤에 오는 명령은 스크립트에 포함됩니다(그리고 스크립트로 대체됩니다).
- 파이프 앞의 명령은 바이너리 실행 파일이며 내부 구조를 제어할 수 없습니다.
또한 stdin을 끄려고 시도했습니다.
( while true; do date; sleep 1; done ) | ( for ((i = 0; i < 3; i++)); do read -r line; echo LINE: $line; sleep 1; done; exec 0<&- )
그러나 조건이 충족되면 stdout으로 아무것도 전송되지 않지만 왼쪽 프로그램은 계속 실행됩니다.
작업을 어떻게 완료할 수 있나요?
답변1
파이프가 종료되지 않는 이유는 명령문이 exit 1
오른쪽 하위 쉘만 종료하기 때문입니다.
오른쪽이 종료되면 date
데이터를 생성하는 데 사용되는 명령은 출력을 어디에도 쓸 수 없기 때문에 실패하기 시작합니다(신호에 의해 종료됩니다 PIPE
). 테스트에 대한 종료 상태가 없으므로 date
이 내용이 표시되지 않습니다.
왼쪽을 다음과 같이 다시 작성하십시오.
(
while true; do
if ! date; then
exit # or: break
fi
sleep 1
done
)
또는
(
while date; do
sleep 1
done
)
그러면 전체 파이프라인이 예상대로 작동하게 됩니다.
이는 파이프 왼쪽에 있는 데이터 생성 바이너리가 쓰기 성공 여부를 확인하지 않고 있음을 나타냅니다(신호 PIPE
도 무시할 수 있음). 오른쪽 서브쉘에서 표준 입력을 닫는 것은 도움이 되지 않습니다(어쨌든 서브쉘을 종료할 때 이미 완료되었습니다).
이는 데이터를 생성한 프로그램의 버그입니다.
이 문제를 어떻게 해결합니까?
한 가지 방법은 명명된 파이프를 사용하는 것입니다. 이를 통해 나중에 데이터 생성 프로세스를 종료하는 데 사용할 수 있는 데이터 생성 프로세스의 PID를 얻을 수 있습니다.
#!/bin/bash
rm -f data.pipe
mkfifo data.pipe
( while true; do date; sleep 1; done ) >data.pipe & pid=$!
(
for ((i = 0; i < 3; i++)); do
read -r line
printf 'LINE: %s\n' "$line"
sleep 1
done
kill "$pid"
exit 1
) <data.pipe
프로그램이 해당 PIPE
신호를 무시하면 다른 신호도 무시할 가능성이 높으므로 먼저 기본 신호 전송을 시도해야 하고 TERM
, 그래도 작동하지 않으면 시도해 보세요 INT
.