web-dev-qa-db-ja.com

キープレスに応答するシェルスクリプト

私は基本的に次のようなことを言うシェルスクリプトを持っています

while true; do
    read -r input
    if ["$input" = "a"]; then 
        echo "hello world"           
    fi
done

それはすべて順調で、良いことですが、ENTERを押す必要があることはこの状況では深刻な問題であることに気付きました。必要なのは、キーを押したときに、Enterキーを押さなくてもスクリプトが応答することです。

シェルスクリプト内でこの機能を実現する方法はありますか?

15
j0h
read -rsn1

1文字だけを期待し(提出を待たずに)、黙って(その手紙を書き戻さないでください)。

21
pacholik

最終的な作業スニペットは次のとおりです。

#!/bin/bash

while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
    echo "hello world"
fi
done
10
Donald Derek

ノンブロッキングの方法でそれを行う別の方法(それがあなたが望むものかどうかわからない)。 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を使用できます。

4
user3442743

この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の組み合わせは、多くのシェルスクリプトをインタラクティブにするために使用できます。これがお役に立てば幸いです。

2
Ian Forsyth

私のプロジェクトでこれを行う方法があります: 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
1
konsolebox