Выйти из канала в bash

Для следующего оператора bash:

tail -Fn0 /tmp/report | while [ 1 ]; do echo "pre"; exit; echo "past"; done

Я получил «pre», но не вышел в командную строку bash, тогда, если я введу что-то в /tmp/report, я смогу выйти из этого скрипта и попасть в командную строку bash.

Я думаю, это разумно. «выход» заставляет оператор «пока» выйти, но «хвост» все еще жив. Если что-то вводится в /tmp/report, «хвост» будет выводиться в канал, затем «хвост» обнаружит, что канал закрыт, затем «хвост» выйдет.

  1. Я прав? Если нет, может ли кто-нибудь дать правильную интерпретацию?
  2. Можно ли добавить что-нибудь в оператор while, чтобы немедленно выйти из всего оператора канала? Я знаю, что могу сохранить pid хвоста во временный файл, затем прочитать этот файл в «пока», а затем убить хвост. Есть ли более простой способ?
  3. Позвольте мне расширить свой вопрос. Если использовать этот хвост в файле скрипта, можно ли выполнить следующие пункты одновременно? а. Если нажать Ctrl-C или подать сигнал главному процессу оболочки, основная оболочка, а также различные подоболочки и фоновые процессы, порожденные основной оболочкой, завершат работу. b. Я мог бы выйти из tail|в то время как только в случае триггера и сохранить другие подпроцессы, продолжающие работать c. Лучше не использовать временный файл или файл канала.

person Qiu Yangfan    schedule 13.12.2013    source источник
comment
возможный дубликат Невозможно завершить оболочку с помощью Ctrl+c   -  person shellter    schedule 13.12.2013


Ответы (2)


Вы можете (ненадежно) уйти от убийства группы процессов:

tail -Fn0 /tmp/report | while :
do 
  echo "pre"
  sh -c 'PGID=$( ps -o pgid= $$ | tr -d \  ); kill -TERM -$PGID'
  echo "past"
done

Это может отправить сигнал большему количеству процессов, чем вы хотите. Если вы запустите приведенную выше команду в интерактивном терминале, все будет в порядке, но в сценарии вполне возможно (действительно вероятно), что группа процессов будет включать сценарий, выполняющий команду. Чтобы избежать отправки сигнала, было бы разумно включить мониторинг и запустить конвейер в фоновом режиме, чтобы убедиться, что для конвейера сформирована новая группа процессов:

#!/bin/sh

# In Posix shells that support the User Portability Utilities option
# this includes bash & ksh), executing "set -m" turns on job control. 
# Background processes run in a separate process group.  If the shell
# is interactive, a line containing their exit status is printed to
# stderr upon their completion.
set -m
tail -Fn0 /tmp/report | while :
do 
  echo "pre"
  sh -c 'PGID=$( ps -o pgid= $$ | tr -d \  ); kill -TERM -$PGID'
  echo "past"
done &
wait

Обратите внимание, что я заменил while [ 1 ] на while :, потому что while [ 1 ] — плохой стиль. (Он ведет себя точно так же, как while [ 0 ]).

person William Pursell    schedule 13.12.2013
comment
Как я тестировал, набор -m имеет значение, в то время как фон или нет кажется одинаковым в обеих ситуациях, в хвосте и в одной и той же новой группе процессов. Не могли бы вы рассказать больше о фоне? - person Qiu Yangfan; 24.12.2013
comment
Стандарт требует, чтобы фоновые конвейеры выполнялись как отдельная группа процессов, если включено управление заданиями. Хотя оболочка может запускать группу переднего плана как другую группу процессов, это не обязательно. - person William Pursell; 25.12.2013

Вы правы. Цикл while выполняется в подоболочке, поскольку его ввод перенаправляется, а exit просто выходит из этой подоболочки.

Если вы используете bash 4.x, вы можете достичь того, чего хотите, с помощью сопроцесса.

coproc TAIL { tail -Fn0 /tmp/report.txt ;}
while [ 1 ]
do
    echo "pre"
    break
    echo "past"
done <&${TAIL[0]}
kill $TAIL_PID

http://www.gnu.org/software/bash/manual/html_node/Coprocesses.html

В более старых версиях вы можете использовать фоновый процесс для записи в именованный канал:

pipe=/tmp/tail.$$
mkfifo $pipe
tail -Fn0 /tmp/report.txt >$pipe &
TAIL_PID=$!
while [ 1 ]
do
    echo "pre"
    break
    echo "past"
done <$pipe
kill $TAIL_PID
rm $pipe
person Barmar    schedule 13.12.2013
comment
Спасибо за вашу помощь! Теперь мои знания растут. Ну у меня баш 3.2.5. Возможно, ваш лучший, а я все еще жду других. - person Qiu Yangfan; 13.12.2013
comment
Вы можете эмулировать сопроцесс, используя фоновый процесс, записывающий в именованный канал. - person Barmar; 13.12.2013
comment
Добавлено решение с использованием именованного канала. - person Barmar; 13.12.2013
comment
Бармар, большое спасибо за вторую версию. На самом деле, я уже пробовал именованный канал. Хотя есть проблема, если слишком много журналов попадает в /tmp/report.txt за короткое время, и действие в while (в моем реальном случае мне нужно какое-то действие в выражении while) не может их быстро обработать достаточно, тогда хвост завершится из-за какой-то ошибки канала, может быть, это проблема с буфером канала, я прав? С хвостом | в то время как стиль, такой проблемы можно было бы избежать, как я пытался. Пока просто обрабатывайте /tmp/report.txt одну строку за другой. Пожалуйста, поправьте меня, если ошибка. - person Qiu Yangfan; 13.12.2013
comment
если труба заполняется, хвост должен просто блокироваться, ожидая, пока читатель не поймает. Он не должен завершаться, пока не завершится процесс чтения. - person Barmar; 13.12.2013
comment
Хорошо, я только что попробовал еще раз, вот результат: В моем случае ошибки: tail -Fn0 /tmp/report.txt ›pipe & while read line ‹pipe; do ... done Затем я получил ошибку: Broken pipe. В вашем правильном случае: это tail -Fn0 /tmp/report.txt & while read line ; do ... done ‹pipe Не могли бы вы объяснить эту разницу? - person Qiu Yangfan; 13.12.2013
comment
Писатель канала получает ошибку, когда читатель закрывает его. Если вы выполняете перенаправление внутри цикла, он каждый раз открывается и закрывается в цикле, и это первое закрытие убивает писатель. - person Barmar; 13.12.2013
comment
Во второй версии конец канала для чтения открывается один раз для всего цикла и закрывается только после завершения цикла. - person Barmar; 13.12.2013