Невозможно остановить скрипт bash с помощью Ctrl + C

Я написал простой скрипт bash с циклом для печати даты и пинга на удаленную машину:

#!/bin/bash while true; do # *** DATE: Thu Sep 17 10:17:50 CEST 2015 *** echo -e "\n*** DATE:" `date` " ***"; echo "********************************************" ping -c5 $1; done 

Когда я запускаю его с терминала, я не могу остановить его с помощью Ctrl + C. Кажется, он отправляет ^ C в терминал, но сценарий не останавливается.

  • Что такое хороший способ сказать «запустить это через 15 минут» на оболочке?
  • как сделать подсказку bash более читаемой в .bashrc - это делать с датой, но даже в целом
  • Как установить несколько программ командной строки java?
  • Grep и игнорирование ведущих пробелов
  • Скопируйте все точечные файлы, кроме `.git` и` ..`
  • Расцветка разветвленных процессов
  •  MacAir:~ tomas$ ping-tester.bash www.google.com *** DATE: Thu Sep 17 23:58:42 CEST 2015 *** ******************************************** PING www.google.com (216.58.211.228): 56 data bytes 64 bytes from 216.58.211.228: icmp_seq=0 ttl=55 time=39.195 ms 64 bytes from 216.58.211.228: icmp_seq=1 ttl=55 time=37.759 ms ^C <= That is Ctrl+C press --- www.google.com ping statistics --- 2 packets transmitted, 2 packets received, 0.0% packet loss round-trip min/avg/max/stddev = 40.887/59.699/78.510/18.812 ms *** DATE: Thu Sep 17 23:58:48 CEST 2015 *** ******************************************** PING www.google.com (216.58.211.196): 56 data bytes 64 bytes from 216.58.211.196: icmp_seq=0 ttl=55 time=37.460 ms 64 bytes from 216.58.211.196: icmp_seq=1 ttl=55 time=37.371 ms 

    Независимо от того, сколько раз я нажимаю его или как быстро я это делаю. Я не могу это остановить.
    Проведите тест и осознайте сами.

    Как побочное решение, я останавливаю его с помощью Ctrl + Z , который останавливает его, а затем kill %1 .

    Что именно происходит здесь с ^ C ?

  • Использование списка с разделителями строк в качестве параметра
  • постоянно устанавливая vim как $ EDITOR для crontab
  • Специальные символы в псевдониме
  • Что такое хороший способ сказать «запустить это через 15 минут» на оболочке?
  • Передача разобранного вывода sed для поиска (в этом направлении)
  • Почему, когда я пытаюсь выполнить программу как обычный пользователь, мне становится отказано, и когда я выполняю его с помощью sudo, я получаю «Not Found»
  • 8 Solutions collect form web for “Невозможно остановить скрипт bash с помощью Ctrl + C”

    Что происходит, так это то, что как bash и ping получают SIGINT ( bash не является интерактивным, как ping и bash работают в той же группе процессов, которая была создана и установлена ​​как группа процессов переднего плана терминала с помощью интерактивной оболочки, из которой вы запускали этот скрипт).

    Тем не менее, bash обрабатывает SIGINT асинхронно, только после выхода из текущей команды. bash выходит только после получения этого SIGINT, если текущая команда запускает SIGINT (т. е. ее статус выхода указывает, что он был убит SIGINT).

     $ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here' ^Chere 

    Выше, bash , sh и sleep получают SIGINT, когда я нажимаю Ctrl-C, но sh обычно выходит с кодом выхода 0, поэтому bash игнорирует SIGINT, поэтому мы видим «здесь».

    ping , по крайней мере, один из iputils, ведет себя так. Когда прерывается, он печатает статистику и выходы с 0 или 1 статусом выхода в зависимости от того, были ли ответны его пинги. Итак, когда вы нажимаете Ctrl-C во время работы ping , bash отмечает, что вы нажимали Ctrl-C в своих обработчиках SIGINT, но поскольку ping обычно выходит из строя, bash не выходит.

    Если вы добавите sleep 1 в этот цикл и нажмите Ctrl-C во время sleep , потому что sleep не имеет специального обработчика на SIGINT, он умрет и сообщит bash что он умер от SIGINT, и в этом случае bash выйдет ( он фактически убьет себя SIGINT, чтобы сообщить о прерывании его родителям).

    Что касается того, почему bash ведет себя так, я не уверен, и я отмечаю, что поведение не всегда детерминировано. Я только что задал вопрос о списке рассылки разработки bash ( Update : @Jilles теперь пригвоздил причину в своем ответе ).

    Единственная другая оболочка, которую я нашел, которая ведет себя аналогично, – ksh93 (Update, как упоминалось @Jilles, так же как и FreeBSD sh ). Там SIGINT явно игнорируется. И ksh93 выходит, когда команда уничтожается SIGINT.

    Вы получаете то же поведение, что и bash но также:

     ksh -c 'sh -c "kill -INT \$\$"; echo test' 

    Не выводит «тест». То есть, он выходит (убив себя SIGINT там), если команда, которую он ожидал, умирает от SIGINT, даже если он сам не получил этот SIGINT.

    Работать вокруг было бы добавить:

     trap 'exit 130' INT 

    В верхней части скрипта вы можете заставить bash выйти после получения SIGINT (обратите внимание, что в любом случае SIGINT не будет обрабатываться синхронно, только после выхода из текущей команды).

    В идеале мы хотим сообщить родителям, что мы умерли от SIGINT (так что, если это, например, другой скрипт bash , этот скрипт bash также прерывается). Выполнение exit 130 – это не то же самое, что умирать от SIGINT (хотя некоторые оболочки будут устанавливать значение $? Для одного и того же значения для обоих случаев), однако он часто используется для сообщения о смерти SIGINT (в системах, где SIGINT – это 2, что больше всего).

    Однако для bash , ksh93 или FreeBSD sh это не работает. Этот статус выхода 130 не считается смертью SIGINT, и родительский скрипт не прерывается там.

    Таким образом, возможно, лучшей альтернативой было бы убить себя SIGINT после получения SIGINT:

     trap ' trap - INT # restore default INT handler kill -s INT "$$" ' INT 

    Объяснение состоит в том, что bash реализует WCE (ожидание и совместный выход) для SIGINT и SIGQUIT по http://www.cons.org/cracauer/sigint.html . Это означает, что если bash получает SIGINT или SIGQUIT, ожидая завершения процесса, он будет ждать, пока процесс не выйдет и выйдет, если процесс завершит этот сигнал. Это гарантирует, что программы, которые используют SIGINT или SIGQUIT в своем пользовательском интерфейсе, будут работать как ожидалось (если сигнал не привел к завершению работы программы, сценарий будет продолжаться в обычном режиме).

    Недостаток появляется с программами, которые захватывают SIGINT или SIGQUIT, но затем завершаются из-за этого, но используя обычный выход (), а не пересылают сигнал самим себе. Возможно, не удастся прервать скрипты, которые вызывают такие программы. Я думаю, что реальное исправление существует в таких программах, как ping и ping6.

    Подобное поведение реализовано ksh93 и FreeBSD / bin / sh, но не большинством других оболочек.

    Как вы догадались, это связано с тем, что SIGINT отправляется в подчиненный процесс, и оболочка продолжает работу после завершения этого процесса.

    Чтобы лучше справиться с этим, вы можете проверить статус выхода для запущенных команд. Код возврата Unix кодирует как метод, с которого завершился процесс (системный вызов, так и сигнал), и какое значение было передано exit() или какой сигнал завершил процесс. Все это довольно сложно, но самый быстрый способ использовать его – знать, что процесс, который был прерван сигналом, будет иметь ненулевой код возврата. Таким образом, если вы проверите код возврата в своем скрипте, вы можете выйти из себя, если дочерний процесс был прерван, устраняя необходимость в неэлементах, таких как ненужные вызовы sleep . Быстрый способ сделать это во всем скрипте – использовать set -e , хотя для его выполнения может потребоваться несколько настроек для команд, состояние выхода которых не ожидается.

    Терминал замечает control-c и отправляет сигнал INT в группу процессов переднего плана, которая здесь включает оболочку, поскольку ping не создал новую группу процессов переднего плана. Это легко проверить, захватив INT .

     #! /bin/bash trap 'echo oh, I am slain; exit' INT while true; do ping -c5 127.0.0.1 done 

    Если запущенная команда создала новую группу процессов переднего плана, тогда элемент управления-c перейдет в эту группу процессов, а не в оболочку. В этом случае оболочке потребуется проверить коды выхода, так как терминал не будет сигнализирован.

    (Обработка INT в оболочках может быть сказочно сложной, кстати, поскольку оболочке иногда приходится игнорировать сигнал, а иногда и нет. Исходное погружение, если это любопытно, или задуматься: tail -f /etc/passwd; echo foo )

    Ну, я попытался добавить sleep 1 к сценарию bash и взломать!
    Теперь я могу остановить его двумя Ctrl + C.

    При нажатии Ctrl + C сигнал SIGINT отправляется в текущий исполняемый процесс, причем эта команда выполнялась внутри цикла. Затем процесс подоболочки продолжает выполнение следующей команды в цикле, которая запускает другой процесс. Чтобы иметь возможность остановить сценарий, необходимо отправить два сигнала SIGINT , один для прерывания текущей команды в исполнении и один для прерывания процесса подоболочки .

    В сценарии без вызова sleep , нажатие Ctrl + C очень быстро и много раз, похоже, не работает, и невозможно выйти из цикла. Я предполагаю, что нажатие дважды недостаточно быстро, чтобы сделать его в нужный момент между прерыванием текущего выполняемого процесса и началом следующего. Каждый нажатый Ctrl + C отправляет SIGINT в процесс, выполняемый внутри цикла, но ни к подоболочке .

    В скрипте со sleep 1 этот вызов приостанавливает выполнение в течение одной секунды, а когда прерван первым Ctrl + C (первый SIGINT ), подоболочка займет больше времени, чтобы выполнить следующую команду. Итак, теперь второй Ctrl + C (второй SIGINT ) перейдет к подоболочке , и выполнение скрипта закончится.

    Попробуй это:

     #!/bin/bash while true; do echo "Ctrl-c works during sleep 5" sleep 5 echo "But not during ping -c 5" ping -c 5 127.0.0.1 done 

    Теперь измените первую строку на:

     #!/bin/sh 

    и повторите попытку – см., если ping теперь прерывается.

     pgrep -f process_name > any_file_name sed -i 's/^/kill /' any_file_name chmod 777 any_file_name ./any_file_name 

    например, pgrep -f firefox будет grep PID запускать firefox и сохранит этот PID в файле с именем any_file_name . Команда «sed» добавит kill в начале номера PID в файле «any_file_name». Третья строка будет any_file_name файл any_file_name . Теперь следующая строка уничтожит PID, доступный в файле any_file_name . Запись этих четырех строк в файле и выполнение этого файла может выполнять Control- C . Работа для меня абсолютно прекрасна.

    Вы являетесь жертвой известной ошибки bash. Bash делает jobcontrol для скриптов, что является ошибкой.

    Что происходит, так это то, что bash запускает внешние программы в другой группе процессов, чем для самого скрипта. Поскольку рабочая группа TTY настроена на группу процессов текущего переднего плана, только этот процесс переднего плана убивается и цикл в сценарии оболочки продолжается.

    Чтобы проверить: выберите и скопируйте недавнюю оболочку Bourne, которая реализует pgrp (1) в качестве встроенной программы, затем добавьте / bin / sleep 100 (или / usr / bin / sleep в зависимости от вашей платформы) в цикл сценария, а затем запустите Bourne Shell. После того, как вы использовали ps (1) для получения идентификаторов процесса для команды sleep и bash, который запускает скрипт, вызовите pgrp <pid> и замените «<pid>» идентификатором процесса сна и bash, который запускает скрипт , Вы увидите различные идентификаторы групп процессов. Теперь вызовите что-то вроде pgrp < /dev/pts/7 (замените имя tty на tty, используемое скриптом), чтобы получить текущую группу процессов tty. Группа процессов TTY равна группе процессов команды sleep.

    Чтобы исправить: используйте другую оболочку.

    Недавние источники Bourne Shell находятся в моем наборе инструментов, который вы можете найти здесь:

    http://sourceforge.net/projects/schilytools/files/

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