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

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

while : do foo done 

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

 while : do foo if [pressed_ctrl_c]: break done 

  • получение имен всех открытых файлов PDF (в evince или иначе)
  • Как повторить команду, когда в файле есть определенная строка?
  • Какие команды Unix можно использовать в качестве семафора / блокировки?
  • Получить вывод из сценария ожидания в переменной
  • Как я могу подавить вывод только в том случае, если команда выполнена успешно?
  • Удобный способ проверить, использует ли система systemd или sysvinit в BASH?
  • Шелл-скрипт, фильтрующий атрибуты файлов jpg
  • Сжатие ls результат с использованием tar
  • 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 читается с клавиатуры, то выше будет нуждаться в пересмотре.

    Linux и Unix - лучшая ОС в мире.