Безопасное выход из цикла в bash

Скажем, у меня есть сценарий bash, который делает:

while : do foo done 

Я хотел бы иметь возможность запускать этот скрипт с консоли и иметь возможность выйти из него в произвольное время, пока это происходит между двумя прогонами foo. Поэтому, если, скажем, я нажимаю Ctrl + C (это может быть другое действие, из-за которого скрипт должен выйти, Ctrl + C – просто пример), который выйдет в следующей доступной точке после выполнения foo:

 while : do foo if [pressed_ctrl_c]: break done 

2 Solutions collect form web for “Безопасное выход из цикла в bash”

Вы можете попробовать такую ​​конструкцию:

 #!/bin/bash # INTR= trap 'INTR=yes; echo "** INTR **" >&2' INT while : do ( # Protect the subshell block trap '' INT # Protected code here echo -n "The date/time is: " sleep 2 date read -t2 -p 'Continue (y/n)? ' YN || echo test n = "$YN" && echo "Asked for BREAK" >&2 && exit 90 ) SS=$? test 90 -eq $SS && echo "Matched BREAK" >&2 && break # Ctrl/C, perhaps? test yes = "$INTR" && echo "Matched INTR" >&2 && break done exit 0 

Некоторые примечания

  • read и test пара демонстрирует интерактивное управление защищенным сегментом кода внутри блока ( ... ) .
  • exit 90 является эквивалентом break но изнутри подоболочки. test 0 != $? ... test 0 != $? ... после того, как конец блока подоболочки будет завершен, чтобы зафиксировать статус exit 90 и реализовать break который действительно нужен коду.
  • Подоболочка может использовать разные значения статуса выхода для указания различных типов требуемого потока управления ( break , exit и т. Д.).
  • Это не мешает программе устанавливать собственный обработчик сигнала. Например, gdb устанавливает собственный обработчик для SIGINT ( Ctrl C ). Если целью является предотвращение выхода пользователя из сеанса, изменение ключа прерывания может помочь устранить ситуацию (см. Код ниже). Неэлегантный, но потенциально эффективный.

Изменение ключа SIGINT на терминале

 G=$(stty -g) # Save settings test -n "$G" && stty intr ^A # That is caret and A, not Ctrl/A # ... SIGINT generated with Ctrl/A rather than Ctrl/C ... test -n "$G" && stty "$G" # Restore original settings 

Это похоже на работу:

 #!/bin/sh pressed_ctrl_c= trap "pressed_ctrl_c=1" INT while true do (trap "" INT; foo)& wait || wait if [ "$pressed_ctrl_c" ] then # echo break fi done 
  • Инициализировать pressed_ctrl_c в значение null. Это, вероятно, не обязательно в скрипте.
  • trap command signum сообщает оболочке, что она настроена на фиксацию сигнального номера signum и выполняет command когда она ее ловит. «INT» сокращен для «SIGINT», который является коротким для «сигнала прерывания», который является техническим термином для сигнала, генерируемого Ctrl + C , поэтому при pressed_ctrl_c Ctrl + C оболочки pressed_ctrl_c на 1. (SIGINT имеет числовое значение 2, поэтому вы можете сказать trap "pressed_ctrl_c=1" 2 если вы хотите сэкономить при вводе текста.)
  • Внутри цикла у нас есть командная строка в круглых скобках. Это создает суб-оболочку.
  • Мы снова используем команду trap . Эта command времени является пустой строкой; это говорит оболочке игнорировать сигнальное число signum . Поскольку это находится в круглых скобках, это влияет только на подоболочку.
  • Запустить foo . Поскольку он был запущен из подоболочки, foo будет игнорировать Ctrl + C , т. Е. Он будет продолжать работать, даже если вы его foo .
  • Поместите подоболочку на задний план ….
  • … и дождитесь его завершения.
  • Если команда wait успешно, перейдите к оператору if . Если это не удается, выполните другой. (Я вернусь к этому.)
  • Если $pressed_ctrl_c установлено, вырваться из цикла. Необязательно раскомментируйте команду echo если Ctrl + C появляется на вашем терминале как ^C и вы хотите перейти к следующей строке.

Мы запускаем команду в фоновом режиме, а затем сразу же wait ее. Это очень похоже на запуск команды на переднем плане (по крайней мере, когда это делается в скрипте). Команда wait завершится успешно, когда подоболочка завершится; т.е. когда команда foo завершается. (Команда wait будет успешно завершена, даже если foo вернет ошибку.) Когда первая команда wait завершается успешно, мы пропускаем вторую и переходим к if .

Оболочка, которая запускает цикл, перехватывает прерывания, но подоболочка и, следовательно, процесс foo игнорируют их. Итак, когда вы pressed_ctrl_c Ctrl + C , оболочка устанавливает pressed_ctrl_c в 1 и прерывает команду (первый) wait . Поскольку первая команда wait не удалась, мы переходим ко второму. Помните, что foo по-прежнему работает, поэтому это wait все еще есть чем заняться (т. Е. Он будет ждать завершения foo ).

Наконец, если переменная была установлена, чтобы указать, что Ctrl + C была нажата во время работы foo , отключите вывод цикла.

Если вы дважды нажмете Ctrl + C , вторая будет прервать и прервать вторую команду wait . Это приведет к тому, что ваш скрипт завершится и вернет вас в приглашение вашей оболочки, оставив foo в фоновом режиме. Вы можете смягчить это, сказав wait || wait || wait || wait wait || wait || wait || wait wait || wait || wait || wait ; расширяя его, насколько вы хотите. Вам нужно будет набирать Ctrl + C один раз для каждого wait преждевременно завершая сценарий.

D'о!

Проблема в том, что процессы, которые помещаются в фоновом режиме с помощью сценария, имеют свой стандартный вход, установленный в /dev/null . Если ваш foo читается с клавиатуры, то выше будет нуждаться в пересмотре.

  • Создать мигающий текст с эхо-символами
  • Безопасный способ установки сценария .sh в / usr / local / bin?
  • Вывод переменной скрипта Bash
  • Запуск и остановка нескольких исполняемых файлов для легкой отладки
  • просмотр printf после трубы (или подоболочки?)
  • Слияние двух файлов с помощью скриптов
  • сценарий оболочки для создания каталогов, подкаталогов и файлов по шаблону
  • Sudo в качестве другого пользователя, чтобы сохранить свой домашний каталог в txt-файле
  • Как получить доступ к содержимому файла, который используется в качестве аргумента при запуске сценария bash?
  • Сохранить результат grep для массива
  • Автоматизировать процедуру ввода двоичного файла
  • Запуск программы (команды) на терминале после экрана входа в Ubuntu 14.04
  • Linux и Unix - лучшая ОС в мире.