파이프를 통해 데이터를 수신하는 스크립트에서 다른 프로그램을 종료하는 방법은 무엇입니까?

파이프를 통해 데이터를 수신하는 스크립트에서 다른 프로그램을 종료하는 방법은 무엇입니까?

다른 프로세스에서 파이프를 통해 전송된 데이터를 처리하는 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.

관련 정보