'trap'をselectループと組み合わせて使用すると、つまり、オプションが表示されているときにCTRL + Cを押してブレークアウトしようとすると、ターミナルに^ Cが出力されます。スクリプトから「トラップ」を削除すると、通常は終了します。つまり、CTRL + Cを受け入れます。
これを2つの異なるバージョンのbash(1つはCentOSに付属、もう1つはFedoraに付属)でテストしましたが、Fedora(4.4.23(1)-リリース)のものに問題があります。 Bashバージョン4.2.46(2)-CentOSに同梱されているリリースは正常に動作しているようです。ローカル端末とリモート(ssh経由)でもこれをテストしました。そして問題は常にFedora側にあります。
私が話していることを確認するためにコードを投稿します
これは機能しません:
#!/bin/bash
trap exit SIGINT
select opt in One Two Three; do
break
done
'trap exit SIGINT'行全体を削除した場合、問題なく動作し、CTRL + Cを受け入れます。
これを修正またはバイパスする方法はありますか?
これを修正またはバイパスする方法はありますか?
_--posix
_オプションを使用するか、一時的に_set -o posix
_を使用して、posixモードをオンにすることでバイパスできます。
_set -o posix
select opt in foo bar baz; do
echo "opt=$opt"
done
set +o posix
_
この動作の説明については、 zread()
関数を参照してください。これはread
ビルトイン(select
のbashによって内部的にも呼び出されます)によって使用されます。
_ 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の「再起動」に関する詳細read
組み込み ここ 。
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
ループの本体が実行される前に実行されます。
トラップハンドラーはnotを押すと実行されます Enter 後 Ctrl+C 押すので Enterselect
プロンプトで、メニューが再表示されます。