Рассматриваем пользовательское программное обеспечение командной строки (например, loopHelloWorld), которое обнаруживает ctrl+C для хорошего завершения работы. Как передать ответ, не теряя Ctrl+C
?
$ loopHelloWorld
- Ctrl+C to nicely shutdown
Но с pipe, pipe убивает программное обеспечение без корректного завершения работы
$ 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
и запущенный подоболочкой, которая запускает while
цикл, получат SIGINT
.
Если loopHelloWorld
пишет Ctrl+C to nicely shutdown
сообщение на свой stdout, оно также будет записано в pipe. Если этопослеподоболочка на другом конце уже умерла, тогда loopHelloWorld
будеттакжеполучите сигнал SIGPIPE, с которым вам нужно будет справиться.
Здесь вы должны записать это сообщение в stderr, поскольку это не обычный вывод вашей команды ( ping
хотя это не относится к примеру). Тогда оно не пройдет через конвейер.
Или вы можете заставить подоболочку, выполняющую цикл while, игнорировать SIGINT, чтобы она продолжала считывать loopHelloWorld
вывод после SIGINT:
loopHelloWorld | (
trap '' INT
while IFS= read -r line; do
printf '%s\n' "$line"
done
)
Однако это приведет к тому, что при нажатии кнопки статус выхода конвейера будет равен 0 Ctrl+C.
Другим вариантом для этого конкретного примера было бы использование zsh
или ksh93
вместо bash
. В этих оболочках while
цикл будет выполняться в основном процессе оболочки, поэтому не будет затронут SIGINT.
Это не поможет, 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