選択ループで「トラップ」を使用すると、つまりオプションが表示されている間にCTRL + Cを押して中断しようとすると、端末に^ Cのみが印刷されます。スクリプトから「トラップ」を削除すると正常に終了します。つまり、Ctrl + Cを使用できます。
私はこれを2つの異なるバージョンのbash(CentOSに付属のバージョンとFedoraに付属のバージョン)でテストしましたが、Fedoraのバージョン(4.4.23(1)-release)に問題があります。 CentOSに付属のBashバージョン4.2.46(2)リリースは正常に動作しているようです。また、端末でローカルにテストし、SSH経由でリモートでテストしました。問題は常にFedoraにあります。
私が言っていることを確認するためにコードを投稿します。
これはうまくいきません。
#!/bin/bash
trap exit SIGINT
select opt in One Two Three; do
break
done
完全な「トラップ終了SIGINT」行を削除すると、問題なくCTRL + Cを使用でき、正しく機能します。
この問題を解決またはバイパスする方法についてのアイデアはありますか?
答え1
この問題を解決またはバイパスする方法についてのアイデアはありますか?
--posix
optionsを使用するか、一時的にposixモードをオンにすると、それをバイパスできます set -o posix
。
set -o posix
select opt in foo bar baz; do
echo "opt=$opt"
done
set +o posix
この動作の説明を表示するには確認できます。zread()
組み込み関数で使用される関数read
(bashから内部的に呼び出されることもありますselect
):
while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
/* XXX - bash-5.0 */
/* We check executing_builtin and run traps here for backwards compatibility */
if (executing_builtin)
check_signals_and_traps (); /* XXX - should it be check_signals()? */
else
check_signals ();
何らかの特別な理由により、組み込み関数が明示的に呼び出されたときにのみ設定され、によって呼び出されるときは設定されませんexecuting_builtin
。これはバグのように見え、意図的なものではありません。read
select
posixモードで実行すると、信号は無効になりますread
。この場合、zreadintr()
呼び出されるのとは異なり、zread()
中断されたシステムコールはトラップ実行後に再度呼び出されません。read(2)
バラよりbuiltins/read.def
:
if (unbuffered_read == 2)
retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr);
else if (unbuffered_read)
retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
else
retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
Bashの「restart」組み込みコマンドのread
詳細ここ。
答え2
マニュアルの関連部分は次bash
のとおりです(少なくともそのように動作すると思います)。
コマンドが完了するのを待ってトラップが確立されたことを
bash
知らせると、コマンドが完了するまでトラップは実行されません。
したがって、トラップハンドラはコマンドが完了するのを待っているselect
ため、ループ本体が実行されるまで呼び出されません。bash
入力が受信されると、select
トラップハンドラが実行されます。
次の修正スクリプトはこれをよりよく示しています。
#!/bin/bash
trap 'echo INT;exit' SIGINT
select opt in One Two Three; do
printf 'Got %s (%s)\n' "$REPLY" "$opt"
done
bash
5.0.3で実行し、を選択し、を押して1
をCtrl+C選択Enterします3
。
$ bash script.sh
1) One
2) Two
3) Three
#? 1
Got 1 (One)
#? ^C
1) One
2) Two
3) Three
#? 3
INT
トラップハンドラは、現在の入力(3
)が承認され、前のループ本文が実行されると実行されます。select
トラップハンドラはいいえEnterプロンプトでボタンを押すとメニューが再び表示されるCtrl+Cため、ボタンを押すと実行されます。Enterselect