右矢印キーが押されたことを検出するbashscript


9

キーコードが右矢印キーでない場合でも、これが常にtrueとして検出されるのはなぜですか?

stty_state=`stty -g`
stty raw; stty -echo
keycode=`dd bs=1 count=1 2>/dev/null`
stty "$stty_state"  

echo $keycode

if [ "$keycode"=39 ]; then
echo "Right Arrow Key Pressed!"
fi

回答:


16

(おそらく)最初に2バイト以上を読み取ります。$keycode矢印キーを押すと、スクリプト内でESCになります。

矢印キーは次のとおりです。

\x1b + some value

条件式にスペースがないため、常にtrueと評価されます。

編集:そのステートメントの更新。

あなたはifの終了ステータスで動作する[コマンド。[コマンドは同等ですtestコマンドであるということはとても重要なことです。コマンドとしては、引数の間にスペースが必要です。この[コマンドは]、最後の引数として必要になるという点でさらに特別です。

[ EXPRESSION ]

コマンドは、EXPRESSIONによって決定された状況で終了します。1または0、trueまたはfalse

括弧を書くのはエキゾチックな方法ではありません。つまり、Cの例のように、構文の一部ではありませんif

if (x == 39)

沿って:

if [ "$keycode"=39 ]; then

あなたが発行する:

[ "$keycode"=39 ]

に拡大する

[ \x1b=39 ]

ここ\x1b=391つの引数として読み取られます。ときtest[与えられた1つのそれが偽で終了引数場合にのみ、式がnullである-になるだろうことはありませんされています。$keycode空であったとしても、結果は=39(null /空ではない)になります。

それを見る別の方法はあなたが言うことです:

if 0 ; then # When _command_ exit with 0.
if 1 ; then # When _command_ exit with 1.

詳細は、これらの質問と回答を読む-だけでなく、上の議論[[[

その点で、バックティックを調査することもできます `` vs $( )


矢印キーを使用したマルチバイトエスケープシーケンス:

冒頭で述べたように、あなたは(おそらく)最初に2バイト以上を読み取ります。$keycode矢印キーを押すと、スクリプト内でESCになります。

矢印やその他の特殊キーを使用すると、エスケープシーケンスがシステムに送信されます。ESCのバイト信号があること、「ここに異なって解釈されなければならないいくつかのバイトが来ます」。ASCIIになり、矢印キー用として[ASCIIが続くABCまたはD

つまり、矢印キーを処理する場合は、3バイトを解析する必要があります。

あなたはこれの方向に何かを試して確認することができます:

{   stty_state=$(stty -g)
    stty raw isig -echo
    keycode=$(dd bs=8 conv=sync count=1)
    stty "$stty_state"
} </dev/tty 2>/dev/null
printf %s "$keycode" | xxd

産出:

HEX        ASCII
1b 5b 41   .[A # Up arrow
1b 5b 42   .[B # Down arrow
1b 5b 43   .[C # Right arrow
1b 5b 44   .[D # Left arrow
 |  |  |
 |  |  +------ ASCII A, B, C and D
 |  +--------- ASCII [
 +------------ ASCII ESC

これがどれほど移植可能かはわかりませんが、以前はこのようなコードをいじって、矢印キーをキャッチしていました。押しqて終了します:

while read -rsn1 ui; do
    case "$ui" in
    $'\x1b')    # Handle ESC sequence.
        # Flush read. We account for sequences for Fx keys as
        # well. 6 should suffice far more then enough.
        read -rsn1 -t 0.1 tmp
        if [[ "$tmp" == "[" ]]; then
            read -rsn1 -t 0.1 tmp
            case "$tmp" in
            "A") printf "Up\n";;
            "B") printf "Down\n";;
            "C") printf "Right\n";;
            "D") printf "Left\n";;
            esac
        fi
        # Flush "stdin" with 0.1  sec timeout.
        read -rsn5 -t 0.1
        ;;
    # Other one byte (char) cases. Here only quit.
    q) break;;
    esac
done

(マイナーな注意点としてはあなたにも()に小数点以下39に対してテストつもり- 。10進数と16進数の間の重複が整理のように見えるエスケープシーケンスの最初のバイトは、ASCII値ESCで、小数点以下 27と16進0x1bながら、小数点以下 39は16進数です0x27。 )


2
質問の最初の問題は、=テスト内の記号を囲むスペースがないため、空ではない文字列として単に解析されるため、trueです。矢印キーが複数バイトであるという事実は別の問題です。
wurtel

2
うーん、その文には現状のコンテキストはあまりありません。すでに知っているマルチバイトシーケンスの説明の一部だと思ったので、読みました。
wurtel

2
@wurtel:はい。漠然と思います。1つの説明[が組み込みのコマンドであると、人々はなぜスペースがはるかに迅速に重要であるのかを理解します。(括弧の代わりに括弧を使用するのは単にbashの奇妙な方法ではありません。)今すぐ実行する必要があります。一度更新してください。
user367890
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.