シェルスクリプトで特殊なキーの入力を検出する

Shell Script

こんにちは、山田ハヤオです。

ターミナルというのは非常に奥が深く、遊んでると結構楽しいものです。

シェルスクリプトで矢印キーを用いたメニューを実装したいと思い、手始めに矢印キーの検知を実装してみました。

日本語情報はほぼ無く、RadditやStack Overflowを参考にしました。

最終的に、仕組みをほぼ理解していなくても使える汎用できなものができました。

この記事のコードを使って最終的にできたのが、これらです。

スポンサーリンク

ソースコード

Bash 5.xで動作することを確認しています。POSIXは無視してます。

capture_special_keys(){
    local SELECTION rest
    IFS= read -r -n1 -s SELECTION
    if [[ $SELECTION == $'\x1b' ]]; then
        read -r -n2 -s rest
        SELECTION+="$rest"
    else
        case "$SELECTION" in
            "")
                echo "Enter"
                ;;
            $'\x7f')
                echo "Backspace"
                ;;
            $'\x20')
                echo "Space"
                ;;
            *)
                read -i "$SELECTION" -e -r rest
                echo "$rest"
                ;;
        esac
        return 0
    fi

    case $SELECTION in
        # backspace 
        $'\x1b\x5b\x41') #up arrow
            echo "Up"
            ;;
        $'\x1b\x5b\x42') #down arrow
            echo "Down"
            ;;
        $'\x1b\x5b\x43') #right arrow
            echo "Right"
            ;;
        $'\x1b\x5b\x44') #left arrow
            echo "Left"
            ;;
    esac
}

使い方

上の関数をスクリプトに貼り付けてから、こんな感じで使います。

entered_key="$(capture_special_keys)"

そうすると入力待機に入り、入力されたキーの名前がentered_keyに代入されます。

スペースキーなら「Space」、バックスペースキーなら「BackSpace」という感じです。

物理的なキー返される値
エンターEnter
スペースキーSpace
バックスペースキーBackSpace
上矢印(↑)Up
下矢印(↓)Down
右矢印(→)Right
左矢印(←)Left

普通のキーが入力された場合

普通の数字や英字が入力された場合、通常のreadコマンドと同じふるまいをしようとします。

この処理を無効化して、例外処理などを実行させたい場合は、19~20行目あたりのcase文内の*)内を書き換えてください。

仕組み

readコマンドのオプションの意味

この関数はBashの機能を色々フル活用しているのでその紹介です。

オプション意味
-rバックスラッシュをエスケープ文字として解釈しない
-s入力された文字をターミナルに表示しない
-n<数字>指定された文字数入力された時点でreadコマンドを終了
-eREADLINEライブラリを用いる
-i <Text>デフォルトで入力されている文字を設定(-eと共に使用する)

詳しくはBashのmanページの組み込みコマンドのセクションを参照するといいです。

https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html#:~:text=0%20%E3%81%A8%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82-,read,-%5B%2Ders%5D%20%5B

関数の説明

入力されたキーは特殊なコードとして標準入力に与えられるそうなので、readコマンドでそれらを拾います。

\xb1みたいなこれはどこでどうやって定義されているのかはよくわかりません。

loadkeysとかで設定するキーマップにでもあるんでしょうか?詳しい人いたらどなたか教えてほしいです。

入力されたのが特殊なコードでない場合、通常のreadコマンドと似た感じで動くようにします。

1つ目のread -n1で1文字だけ拾ってしまっているので、caseで処理後の2回目のread時に-iを利用してデフォルト値に設定することで、如何にも最初から入力されていたかのように振る舞います。

使用例

上の動画のメニューのソースコードを貼っておきます。

いくつかファイルがありますが、select.shが一番しっかりしてます。

ライセンス

このページの自分が書いたコードは全てWTFPLで再ライセンスします。

以下ライセンス原文です。

        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
                    Version 2, December 2004 

 Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> 

 Everyone is permitted to copy and distribute verbatim or modified 
 copies of this license document, and changing it is allowed as long 
 as the name is changed. 

            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 

  0. You just DO WHAT THE FUCK YOU WANT TO.

参考サイト

参考にしたサイトを貼っておきます。

エスケープシーケンス
エスケープシーケンスを体感する - ザリガニが見ていた...。
エスケープシーケンス(escape sequence)とは、直訳すれば「エスケープに続く数列(文字コード列)」といった意味合いになると思う。広い意味では、特別な文字表記(エスケープ記号)で始まる一連の文字列を連想してしまう。例えば、\nが改行を表現するのも広い意味ではエスケープシーケンスに含まれる。しかし、ここではもっ...
ANSIエスケープコード - コンソール制御 - 碧色工房
C言語を利用したコンソール制御について。ANSIエスケープコード(エスケープシーケンス)を利用したコンソールの色付けなどについて説明します。
ANSIエスケープシーケンス チートシート - Qiita
ANSIエスケープシーケンスを駆使すれば、こんな感じでターミナルの出力に色を付けたり出力した内容を書き換えたりできる。ゲーム作成初心者がPHPでテト〇ス風ゲームを作ってみた上記記事…
シェルスクリプトでチェックボックス式の選択メニュー
シェルスクリプトでチェックボックス式の選択メニュー. GitHub Gist: instantly share code, notes, and snippets.
How to get the cursor position in bash?
In a bash script, I want to get the cursor column in a variable. It looks like using the ANSI escape code {ESC}[6n is the only way to get it, for example the fo...
How to know difference between [ENTER] and [SPACE] in bash
I want to make a script that functions differently when pressed and differently when pressed . what i tried: #!/bin/bash read -n1 Input if ]; then echo ...

コメント

タイトルとURLをコピーしました