他のプロセスでパイプを介して送信されたデータを処理する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
.