私は基本的に次のようなことを言うシェルスクリプトを持っています
while true; do
read -r input
if ["$input" = "a"]; then
echo "hello world"
fi
done
それはすべて順調で、良いことですが、ENTERを押す必要があることはこの状況では深刻な問題であることに気付きました。必要なのは、キーを押したときに、Enterキーを押さなくてもスクリプトが応答することです。
シェルスクリプト内でこの機能を実現する方法はありますか?
read -rsn1
1文字だけを期待し(提出を待たずに)、黙って(その手紙を書き戻さないでください)。
最終的な作業スニペットは次のとおりです。
#!/bin/bash
while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
echo "hello world"
fi
done
ノンブロッキングの方法でそれを行う別の方法(それがあなたが望むものかどうかわからない)。 sttyを使用して、最小読み取り時間を0に設定できます(その後にstty saneが使用されない場合、ビットは危険です)
stty -icanon time 0 min 0
その後、通常どおりループを実行します。 -rの必要はありません。
while true; do
read input
if ["$input" = "a"]; then
echo "hello world"
fi
done
重要!ノンブロッキングを終了したら、sttyを使用して通常の状態に戻すことを忘れないでください
stty sane
ターミナルに何も表示されない場合、ハングしているように見えます。
Sttyを通常の状態に戻す前にスクリプトを終了するようにctrl-Cのトラップを含めると、入力したものが見えなくなり、端末がフリーズしたように見えるでしょう。
trap control_c SIGINT
control_c()
{
stty sane
}
P.Sまた、スクリプトにsleepステートメントを入れて、CPUを使い果たさないようにすることもできます。これは、可能な限り高速で継続的に実行されるためです。
sleep 0.1
P.S.Sぶら下がっている問題は、私が-echoを使用していたときだけだったようです。将来の問題を回避するためにsttyをデフォルトにリセットすることはまだ良いので、私は答えにそれを残すつもりです。入力した内容を画面に表示したくない場合は、-echoを使用できます。
このgetkey
関数を使用できます:
_getkey() {
old_tty_settings=$(stty -g) # Save old settings.
stty -icanon
Keypress=$(head -c1)
stty "$old_tty_settings" # Restore old settings.
}
_
端末設定(_stty -icanon
_)で「標準モード」を一時的にオフにしてから、標準入力の1バイトを返す-c1オプションで「head」(シェル組み込み)の入力を返します。 「stty -icanon」を含めない場合、スクリプトは押されたキーの文字をエコーし、RETURNを待ちます(必要なものではありません)。 「head」と「stty」は両方ともシェル組み込みコマンドです。キーを押した後、古い端末設定を保存および復元することが重要です。
次に、getkey()を「_case / esac
_」ステートメントと組み合わせて使用して、エントリのリストから対話型のワンキー選択を行うことができます。例:
_case $Keypress in
[Rr]*) Command response for "r" key ;;
[Ww]*) Command response for "w" key ;;
[Qq]*) Quit or escape command ;;
esac
_
このgetkey()/case-esac
の組み合わせは、多くのシェルスクリプトをインタラクティブにするために使用できます。これがお役に立てば幸いです。
私のプロジェクトでこれを行う方法があります: https://sourceforge.net/p/playshell/code/ci/master/tree/source/keys.sh
Key_readonceが呼び出されるたびに単一のキーを読み取ります。特別なキーの場合、特別な解析ループを実行して、それらを解析することもできます。
これは重要な部分です。
if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
KEY[0]=$K
if [[ $K == $'\e' ]]; then
if [[ BASH_VERSINFO -ge 4 ]]; then
T=(-t 0.05)
else
T=(-t 1)
fi
if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
case "$K" in
\[)
KEY[1]=$K
local -i I=2
while
read -rn 1 -d '' "${T[@]}" "${S[@]}" "KEY[$I]" && \
[[ ${KEY[I]} != [[:upper:]~] ]]
do
(( ++I ))
done
;;
O)
KEY[1]=$K
read -rn 1 -d '' "${T[@]}" 'KEY[2]'
;;
[[:print:]]|$'\t'|$'\e')
KEY[1]=$K
;;
*)
__V1=$K
;;
esac
fi
fi
utils_implode KEY __V0