私は次のスクリプトで変数に何かを読むことができることを知っています。変数=読み取りしかし、変数に値を送信するにはEnterキーを押す必要があります。 Enterキーを押さずにキー押し値を変数に送信するには、またはキーを押したときに反応しないようにするにはどうすればよいですか?
答え1
を使用すると、組み込み関数の引数をbash
使用して、改行なしで読み取る文字数を制限できます。-n
read
#!/bin/bash
echo "Ready? [Y/n]: "
read -n 1 y_or_n
echo
case "$y_or_n" in
[Yy]|"")
echo "you said yes"
;;
*)
echo "you said no"
;;
esac
bash
sh
これはasか呼び出すかに関係なく機能しますbash
。
詳細については、help read
またはマンページを参照してください。bash
他のシェルは-n
パラメータをサポートしていない可能性がありますread
。dash
、例えばいいえ。
答え2
シェルに関する問題は、read
合計バイト数を制限するかどうかにかかわらず、read
まだ情報を提供しないことです。ボタン各自read
は一部しか得られない安定数量数値毎回read
1つ取得しようボタン必要に応じてread
入力をブロックするのがおそらく最良の方法ですdd
。以下は、数ヶ月前にdd
ターミナルI / Oをブロックしようとしたときに作成したシェル関数です。
printkeys()( IFS= n=1
set -f -- "$(tty <&2)"
exec <${1##[!/]*};set \\t
_dd()( c=conv=sync,unblock i=ibs=72 b=bs=8
dd bs=7 ${c%,*} | dd $i o$b c$b $c
) 2>/dev/null
trap " trap '' TTOU TTIN
stty '$(stty -g
stty raw isig inlcr)' " INT EXIT
_dd |while ${2:+:} read -r c || exit 2
do case $#:$c in (1:): ;;
(*:?*) set ":%d$@" \
\'${c%"${c#?}"} ;
c=${c#?} ;;
(*) printf "$@" ;
[ 0 -eq $((n=n<4?n+1:0)) ] &&
set '\n\r'||set \\t ;; esac
done
)
上記の場合、端子入力がdd
パイプの両方のプロセスでフィルタリングされていることがわかります。端末が次にstty
設定されています。生ののパターンは、trap
エラーが発生したときに最終状態を復元することも、関数が呼び出さINT
れEXIT
たときに関数の状態に復元することもできます。(おそらくCTRL + Cまたは割り込みキーを使用して実行できます)。存在する生のモードでは、端末は入力が到着するとすぐにすべてのリーダーに入力をフラッシュします(キー入力ごとにキー入力)。一度に1バイトだけプッシュするわけではありません。できるだけ早くバッファ全体をプッシュします。たとえば、上矢印を押すと、キーボードは次のエスケープシーケンスを送信します。
^[[A
...3バイトですが、一度に押し込みます。
dd
指定されたすべての読み取りを満たすように指定されます(設定された高さに関係なく)ibs=
。これは、を使用した設定にもかかわらず、ibs=7
端末が3バイトだけプッシュしても読み取りがまだ完了することを意味します。他のほとんどのユーティリティはこれを処理するのに問題がありますが、dd
これはconv=sync
バイト間の違いを埋めます\0NUL
。したがって、キーボードの上矢印を押すとdd
3バイトが読み取られ、次のバイトに7が書き込まれます。つまりdd
、エスケープシーケンスの 3 バイトと別の 4\0NUL
バイトです。
ただし、シェルの読み取りでこのデータを取得するには再度ブロックする必要があるため、次のデータはdd
入力バッファを72バイトに同期します。しかし実際にはconv=unblock
そうです。unblock
変換により、dd
入力を\n
ewline区切り文字に分割してcbs=
数を取得します。ここでは8ですsync
。unblock
(またはblock
)変換はsでは同期されdd
ませんが、\0NUL
末尾のスペースでは同期されます。したがって、7バイトごとの最初は、2つのdd
バッファ間のパイプにdd
72バイトを書き込みます。最初の数バイトはキーの内容で、次は\0NUL
s、次には65の空白読み取りの終わりです。
もう一つのことは、末尾のスペースを削除することです。これは、各変換ブロックのunblock
末尾に存在できるだけのスペースを占めます。cbs=
したがって、各dd
書き込みはバイト単位で出力に書き込まれるobs=8
ため、各読み取りは9行に書き込まれるため、合計2回の書き込みが出力パイプに書き込まれます。最初の書き込みは、入力パイプから読み取られた7バイトと末尾の改行文字(別のバイト)で構成される最初の行です。次の書き込みは8つの改行(連続書き込み)です。 8バイトが多いです。なぜならdd
、8つの変換ブロックのそれぞれが8つの空白をすべて占めるからです。
dd
両方のsaシェルの反対側をwhile
1行ずつ繰り返しますread
。空行を無視できます。なぜなら、ターミナルはオプションに従ってすべての改行を出力のキャリッジリターンに変換するからですinlcr
stty
。ただし、$c
シェルの入力がその値を入力した後にシングルバイトでも検出すると、キーボードがキー入力ごとに多くのバイトを送信しない限り、キーを押すだけでフルキーをread
押すこともできます。 7バイト以上(ただ別のブロック要素が必要ですが)。
シェルにキー押下がある場合は、$c
バイト単位で繰り返し、'
文字で区切られた配列に分割し、printf
各バイトの10進値を一度に取得します。$c
関数を実行すると、次のような出力が表示されます。
a:97 b:98 c:99 d:100 e:101
f:102 ;:59 ^M:13 ^M:13 ^M:13
s:115 a:97 d:100 f:102 :32
':39 ':39 ':39 a:97 s:115
d:100 f:102 ;:59 ':39 ^[[A:27:91:65
^[[D:27:91:68 ^[[B:27:91:66 ^[[C:27:91:67 ^[[D:27:91:68 ^[[C:27:91:67
^[[A:27:91:65 ^[[D:27:91:68 ^[[C:27:91:67 ^[[B:27:91:66 ^[[D:27:91:68
^[[C:27:91:67 ^[[A:27:91:65 ^[[D:27:91:68 ^[[C:27:91:67 ^[[B:27:91:66
シェルが変数の値を入力で埋めると、キーが押されるのを防ぐために挿入されたsが消えている\0NUL
ため、sをシェル変数に入れることはできません。dd
\0NUL
(しかし、どんなシェルでもzsh
- この場合はまだ次のように設定できます)。私が知る限り、これはすべてのシェルで動作しなければならず、そしてでもbash
動作dash
しますksh93
。マルチバイト入力も安定して処理します。しかし、それは大胆ではありません。
上記のデモ出力では、作成されるたびに関数の実際の出力の前に作成されなかった追加情報が表示されます。:
上記の順序で最初に表示される前に表示される各文字は実際には端末文字であり、使用または設定echo
できます。残りは関数の出力として印刷されます。ご覧のとおり、入力直後に印刷されます。stty echo
-echo