trashコマンドを拡張
している時に、選択画面で上下するときにVimみたくCtrl-F
で1画面進む、みたいなことしたいな、と思って
Controlキーを含んだ入力をread
で読み取って扱う方法を取り入れたのでそのメモ。
入力キー1つを受け取る
入力キーを1つ受け取るにはBashでは
read -s -n 1 c
Zshでは
read -s -k 1 c
とします。
スクリプトがこの時点まで進んだ時に入力待ちになって、
何か1つでもキーを押すと、そのキーがc
に入ります。
必要なら直前に
echo -n "input: "
とでも出しておけばわかりやすかったり。
後は何でもいいのでc
を見てスクリプトを進めていきます。
1 2 3 4 5 6 7 8 9 10 11 |
|
Controlと同時押しのキーを受け取る
通常の文字列ならばa
とかで良いわけですが、
Ctrl-Aみたいな場合には、それによって送られるASCIIコードで
直接判断します。
Bashのmanを見てみるとQUOTING
の部分に
$'string'
と書くと、ANSI C standard
に変換される、とあります1。
Words of the form $'string' are treated specially. The word expands to
string, with backslash-escaped characters replaced as specified by the
ANSI C standard. Backslash escape sequences, if present, are decoded
as follows:
\a alert (bell)
\b backspace
\e an escape character
\f form feed
\n new line
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\' single quote
\nnn the eight-bit character whose value is the octal value
nnn (one to three digits)
\xHH the eight-bit character whose value is the hexadecimal
value HH (one or two hex digits)
\cx a control-x character
(man bashより引用)
これを使えば、 各キーとControlキーの組み合わせのASCIIコードが分かればそれと比べて 判定することが出来ます。
追記: 2014/07/30
上のmanをここまで引いておいて馬鹿な話ですが、
最後の行に\cx
でcontrol-x
、と書いてあります。
なので、わざわざキーコードなんて調べなくても
\ca
でCtrl-aが得られます。
他のANSI Cのコードなどはここ参照:
追記ここまで
コードは16進数では0x00がCtrl-@ (NULL)に割り当てられていて、 0x01から0x1aまでの26個がCtrl-a~zになります。
追記: 2014/09/22
Ctrl-Spaceは通常Ctrl-@と同じ NULLに割り当てられています。
なので上のエスケープの例でも$'\c@'
と$\c '
は同じとみなされます。
追記ここまで
数えるのが面倒なら
echo -n ^A|xxd
とか
echo -n ^A|od -x
とかで調べられます(^AはCtrl-vCtrl-aで出力)。
これを使って
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
みたいにControlキーを受けて判断する事が出来る様になります。
Tips
8進数または16進数で書くときの注意
コードは上のmanにあるように8進数でも16進数でも書けるわけですが、 書式にしたがって8進数なら
\1
\01
\001
はOK。上にあるように4つ以上数字を入れると正しく判断してくれません。
また16進数なら
\x1
\x01
はOK。3文字以上つかって\x001
や普段16進数を書く時みたいに\0x01
みたいに書くと正しく
判断してくれないので、特に16進数の時は注意です(自分が軽くハマっただけですが。。。)。
Ctrl-Cを使いたい場合
Ctrl-Cは終了シグナルを送るので直接使えません。
ちょっと使い方が変わりますが、
trap "echo input is Ctrl-C" 2
の様にtrapで引っ掛けて上げることなら出来ます。
水平タブ(Ctrl-I)等を使いたい場合
水平タブ(\t
、Ctrl-I、0x09)は区切り文字となってしまって
これを入れると何も入力されてない、と判断されますが、これを使いたい場合は
orig_ifs=$IFS
IFS=
read -s -n 1 c
IFS=$orig_ifs
の様にIFSを空白だけにして、タブを区切り文字から外してあげれば使える様になります。
使えない文字
上の様にIFSをなんとかしても Ctrl-J/M(改行、復帰)は何も送られないまま キーが閉じられたとみなされてNULL(0x00)と判断されます。
Enterをいきなり押しても同じ。
また、Ctrl-Y/Z(媒体終端/置換)はスクリプトの上から 中断信号を送るので捉えることが出来ません。
この辺りは直接ターミナルの設定を無理やり弄らないと使えません。
Ctrl-SなんかもそのままだとXOFFに割り当てられてて 表示の中断になってしまって使えません2 。
ESCを判断する
ASCIIコードが使えるのでESC(Ctrl-[)も判断できます。
ESCは$'\x1b
または$'\e'
で判定できます。
まとめ
この$
の後にシングルクォートを使って$'~'
みたいな形で書くのは
昔、たまにみては'$~'
の書き間違えか?とか勝手に思ってたんですが、
書いてある通りに書かないと動かないコードがあって困ったことがあります。
googleに聞くにもなんと聞いたら良いのかが難しいので。
まずman見てみろというのが身にしみる感じですが、
manでも検索するにもある程度知らないと探しようがない感じなので。
(Bashくらいのmanは一度きちんと全部読んで理解しておかないといけないな。。。
というだけですが)
-
$
の後をシングルクォートで囲うことでコードを送ることが出来るようになります。 echoなんかでも$ echo aaa$'\n'aaa aaa aaa $
みたいに使えます。 ちなみに
\n
は16進数だと0A
(Ctrl-J)に割り当てられてるので$ echo aaa$'\x0a'aaa aaa aaa $
としても一緒。 ↩
-
Ctrl-Sはコマンド履歴のインクリメンタルサーチ で使いたいのでOFFにしてる人が多いと思いますが。