bash 명령을 파이프하고 Ctrl+C를 계속 작동시키는 방법은 무엇입니까?

bash 명령을 파이프하고 Ctrl+C를 계속 작동시키는 방법은 무엇입니까?

적절한 종료를 위해 ctrl+C를 감지하는 사용자 정의 명령줄 소프트웨어(예: loopHelloWorld)를 고려해보세요. 을 잃지 않고 답변을 파이프하는 방법은 무엇입니까 Ctrl+C?

$ loopHelloWorld
    - Ctrl+C to nicely shutdown

그러나 파이프를 사용하면 파이프가 제대로 종료되지 않고 소프트웨어를 종료합니다.

$ loopHelloWorld | 
    while IFS= read -r line; do
        echo "$line"
    done

ping example.com |
  while IFS= read -r line; do
    echo "$line"
  done

답변1

Ctrl+C파이프라인의 모든 프로세스에 SIGINT가 전송됩니다(모두 대화형 셸의 포그라운드 작업에 해당하는 동일한 프로세스 그룹에서 실행되기 때문입니다).

그래서:

loopHelloWorld | 
  while IFS= read -r line; do
    echo "$line"
  done

실행 중인 프로세스 와 루프를 loopHelloWorld실행하는 하위 쉘을 실행하는 프로세스 모두 .whileSIGINT

loopHelloWorldstdout에 메시지를 쓰면 파이프 Ctrl+C to nicely shutdown에도 기록됩니다. 그렇다면~ 후에다른 쪽 끝에 있는 서브쉘은 이미 죽었고, loopHelloWorld그러면또한처리해야 하는 SIGPIPE를 받습니다.

여기서는 명령의 일반 출력이 아니기 때문에 해당 메시지를 stderr에 작성해야 합니다( ping단, 예제에는 적용되지 않음). 그러면 파이프를 통과하지 못할 것입니다.

또는 while 루프를 실행하는 서브셸이 SIGINT를 무시하도록 하여 loopHelloWorldSIGINT 이후의 출력을 계속 읽을 수 있습니다.

loopHelloWorld | (
  trap '' INT
  while IFS= read -r line; do
    printf '%s\n' "$line"
  done
)

그러나 를 누르면 파이프라인의 종료 상태가 0이 됩니다 Ctrl+C.

특정 예에 대한 또 다른 옵션은 대신 zsh또는 를 사용하는 것입니다 . 해당 셸에서 루프는 기본 셸 프로세스에서 실행되므로 SIGINT의 영향을 받지 않습니다.ksh93bashwhile

이는 포그라운드 프로세스 그룹에서 loopHelloWorld | cat어디에서 실행 cat되는지 에 대해서는 도움이 되지 않습니다 .loopHelloWorld

답변2

트랩을 사용하십시오.

#! /bin/bash

trap handleInt SIGINT

interrupted=0

function handleInt {
    echo "Interrupted..."
    interrupted=1
}


while true
do
    [[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
    echo "Sleeping..."
    sleep 1
    read -r line
    echo "$line"
done

관련 정보