Ctrl + Cを使用してbashスクリプトを停止することはできません。

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

ベストアンサー1

何が起こるかはSIGINTbashです。pingbashいいえ対話型、両方ともスクリプトを実行する対話型シェルによって生成され、端末の前景プロセスグループとして設定された同じプロセスグループpingで実行されます。bash

ただし、bash対応するSIGINTは、現在実行中のコマンドが終了した後にのみ非同期的に処理されます。bash現在実行中のコマンドがSIGINTによって終了した場合(つまり、終了状況がSIGINTによって終了したことを示す)、SIGINTが受信されたときにのみ終了します。

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

上でbashCtrl-C を押すと SIGINT が受信されますが、sh正常に 0 終了コードで終了するため、SIGINT は無視されるので「ここ」が表示されます。sleepshbash

ping、少なくともiputilsが動作する方法です。中断すると、統計情報が印刷され、ping 応答の有無によって終了ステータス 0 または 1 で終了します。したがって、ping実行中にCtrl-Cを押すとSIGINTハンドラがbash押されましたが、正常に終了するため終了しないことがわかります。Ctrl-Cpingbash

sleep 1そのループに1つを追加し、実行中Ctrl-Cに押すとSIGINTに特別なハンドラがないため、SIGINTで死んでSIGINTで死んだと報告します。この場合、終了します(実際にはSIGINTで自己終了します。順番に)、親エントリに割り込みを報告します。)sleepsleepbashbash

この動作が発生する理由は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

スクリプトの上部でbashSIGINT を受け取ると強制終了します。 SIGINTは、いかなる場合でも同期的に処理されず、現在実行中のコマンドが終了した後にのみ処理されます。

理想的にはSIGINTで死亡したことを親に報告したいと思います。したがって、他のbashスクリプトの場合、そのbashスクリプトも中断されます。 anを実行することはexit 130SIGINTで死ぬのと同じではありませんが(一部のシェルは両方の場合に$?同じ値を設定しますが)、SIGINTが死ぬことを報告するためによく使用されます(SIGINTが2のシステムではこれが最も多いです)。

しかし、FreeBSDbashksh93FreeBSDではshこれは機能しません。 130 終了ステータスは SIGINT によって終了として処理されず、親スクリプトはいいえそこを止めなさい。

したがって、おそらくより良いオプションは、SIGINTを受け取った後にSIGINTで自殺することです。

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

おすすめ記事