GNU/Linux >> Znalost Linux >  >> Linux

Nelze zastavit Bash skript pomocí Ctrl+c?

Napsal jsem jednoduchý bash skript se smyčkou pro tisk data a ping na vzdálený počítač:

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

Když to spouštím z terminálu, nejsem schopen to zastavit pomocí Ctrl+C .
Zdá se, že odesílá ^C do terminálu, ale skript se nezastaví.

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

Bez ohledu na to, kolikrát to stisknu nebo jak rychle to udělám. Nejsem schopen to zastavit.
Udělejte si test a uvědomte si to sami.

Jako vedlejší řešení to zastavím pomocí Ctrl+Z , to jej zastaví a poté kill %1 .

Co se přesně děje s ^C ?

Přijatá odpověď:

Stane se, že oba bash a ping přijímat SIGINT (bash být ne interaktivní, oba ping a bash spustit ve stejné skupině procesů, která byla vytvořena a nastavena jako skupina procesů v popředí terminálu interaktivním prostředím, ze kterého jste spustili skript).

Nicméně bash zpracovává tento SIGINT asynchronně, pouze po ukončení aktuálně spuštěného příkazu. bash ukončí se pouze po přijetí tohoto SIGINT, pokud aktuálně spuštěný příkaz zanikne na SIGINT (tj. jeho stav ukončení indikuje, že byl zabit SIGINTem).

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

Nahoře bash , sh a sleep přijímat SIGINT, když stisknu Ctrl-C, ale sh ukončí se normálně s výstupním kódem 0, takže bash ignoruje SIGINT, což je důvod, proč vidíme „zde“.

ping , alespoň ten od iputils, se tak chová. Při přerušení vytiskne statistiku a ukončí se se stavem ukončení 0 nebo 1 v závislosti na tom, zda byly jeho pingy zodpovězeny nebo ne. Když tedy stisknete Ctrl-C při ping běží, bash poznamenává, že jste stiskli Ctrl-C v jeho ovladačích SIGINT, ale od ping ukončí se normálně, bash neukončí se.

Pokud přidáte sleep 1 v této smyčce a stiskněte Ctrl-C když sleep běží, protože sleep nemá na SIGINTu žádný speciální handler, zemře a ohlásí se bash že zemřel na SIGINT, a v tom případě na bash ukončí se (ve skutečnosti se sám zabije pomocí SIGINT, aby ohlásil přerušení svému rodiči).

Související:#!/bin/bash – žádný takový soubor nebo adresář?

Proč bash chová se tak, nejsem si jistý a podotýkám, že chování není vždy deterministické. Právě jsem položil otázku na bash vývojový e-mailový seznam (Aktualizace :@Jilles nyní ve své odpovědi uvedl důvod).

Jediný další shell, který jsem našel a který se chová podobně, je ksh93 (aktualizace, jak zmínil @Jilles, stejně jako FreeBSD sh ). Zdá se, že SIGINT je zjevně ignorován. A ksh93 ukončí se vždy, když je příkaz SIGINT ukončen.

Získáte stejné chování jako bash výše, ale také:

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

Nevydává „test“. To znamená, že odejde (zabitím se tam pomocí SIGINT), pokud příkaz, na který čekal, zemře na SIGINT, i když sám tento SIGINT neobdržel.

Řešením by bylo přidat:

trap 'exit 130' INT

V horní části skriptu vynutit bash pro ukončení po přijetí SIGINT (všimněte si, že SIGINT nebude v žádném případě zpracován synchronně, pouze po ukončení aktuálně spuštěného příkazu).

V ideálním případě bychom chtěli nahlásit rodičům, že jsme zemřeli na SIGINT (takže pokud je to další bash skript například, že bash skript je také přerušen). Provedením exit 130 není totéž jako umírání SIGINTem (ačkoli některé shelly nastaví $? na stejnou hodnotu pro oba případy), ale často se používá k hlášení úmrtí pomocí SIGINT (na systémech, kde SIGINT je 2, což je nejvíce).

Nicméně pro bash , ksh93 nebo FreeBSD sh , to nejde. Tento stav ukončení 130 není SIGINTem považován za smrt a nadřazený skript by nebyl tam přerušit.

Takže možná lepší alternativou by bylo zabít se pomocí SIGINT po obdržení SIGINT:

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

Linux
  1. základní jméno s mezerami ve skriptu bash?

  2. Povolení bash.sh ke spuštění cronu odepřeno

  3. Nečinný bash skript, dokud nebude zaznamenána událost CTRL+c

  1. Přidání do $PYTHONPATH pomocí bash skriptu

  2. Jak zabít python skript pomocí bash skriptu

  3. Spuštění bash skriptu nebo binárního c na souborovém systému s možností noexec

  1. Který Shell Interpreter spouští skript s No Shebang?

  2. Jak zastavit skript Loop Bash v terminálu?

  3. Příkaz Rm ve skriptu Bash nefunguje s proměnnou?