GNU/Linux >> Linux の 問題 >  >> Linux

Ctrl + cでBashスクリプトを停止できませんか?

日付を出力してリモートマシンにpingを実行するためのループを備えた簡単な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を送信しているようです ターミナルに接続しますが、スクリプトは停止しません。

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で正確に何が起こっているのか ?

承認された回答:

何が起こるかというと、両方の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

上記、bashsh およびsleep Ctrl-Cを押すとSIGINTを受け取りますが、sh 通常は0の終了コードで終了するため、bash SIGINTを無視するため、「ここ」が表示されます。

ping 、少なくともiputilsからのものは、そのように動作します。中断されると、統計情報が出力され、pingが応答されたかどうかに応じて、0または1の終了ステータスで終了します。したがって、ping中にCtrl-Cを押すと 実行中、bash Ctrl-Cを押したことに注意してください SIGINTハンドラー内ですが、ping以降 正常に終了します、bash 終了しません。

sleep 1を追加した場合 そのループでCtrl-Cを押します sleep sleepのため、実行中です SIGINTには特別なハンドラーがないため、終了してbashに報告します。 SIGINTで死亡したこと、その場合はbash 終了します(親に中断を報告するために、実際にはSIGINTで自殺します)。

関連:#!/ bin / bash –そのようなファイルやディレクトリはありませんか?

理由についてbash そのように振る舞いますが、よくわかりません。振る舞いが常に決定論的であるとは限らないことに注意してください。 bashで質問しました 開発メーリングリスト(更新 :@Jillesは、彼の回答に理由を突き止めました。

私が見つけた他の唯一のシェルはksh93です(@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 またはFreeBSDsh 、それは機能しません。その130の終了ステータスは、SIGINTによる死亡とは見なされず、親スクリプトはしません。 そこで中止します。

したがって、おそらくより良い代替策は、SIGINTを受け取ったときにSIGINTで自殺することです:

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

Linux
  1. 引数を指定して bash から Python スクリプトを呼び出す

  2. bash.sh で cron を実行する権限が拒否されました

  3. jenkins パイプラインで bash コマンドを実行する

  1. どのシェルインタープリターがシバンなしでスクリプトを実行しますか?

  2. `set -e`を使用したBashスクリプトは`…&&…`コマンドで停止しませんか?

  3. ターミナルでループバッシュスクリプトを停止する方法は?

  1. BashスクリプトのRmコマンドは変数では機能しませんか?

  2. bashスクリプトにスペースを含むベース名?

  3. CTRL+c イベントが記録されるまで bash スクリプトをアイドル状態にする