POSIXシェルとawkが入力文字を1行ずつ読みずに文字ごとに読み取るようにするにはどうすればよいですか?
ローマ字からガーナへの音訳アプリケーションを作成していますが、awkへの入力を文字ごとにすぐに処理したいと思います。
awkが文字を処理する前にreturnまたはenterを押すことなくこれを行う正しい方法がわかりません。
答え1
シェルスクリプトでこのツールを使用してTTYステータスを操作できますstty
。
まず、stty -g
現在の状態を表す文字列を生成します。他の作業を実行する前に、この出力をキャプチャしてどこかに保存してください。後で、この文字列をstty
TTY設定を復元する唯一の引数として渡すことができます。文字列には引用符は必要ありません。 POSIX標準では、stty -g
シェルスクリプトで引用符で囲む必要のない表現を生成する必要があります。
stty raw
一度に1文字ずつ入力できる生モードに入る方法です。
savetty=$(stty -g)
stty raw
...
stty $savetty
スクリプトがどこかで終了または中断された場合でも、設定を復元するtrap
ハンドラを設定するには、このコマンドを使用することをお勧めします。tty
stty
それでは、このダンスをAwkコードでラップしているとしましょう。一般性を失うことなく、Awkの外でこれを行いましょう。 awkに一度に1文字ずつ読み込むにはどうすればよいですか?
awkは暗黙的なスキャン戦略または演算子を使用して行を読み取ることができますgetline
。getchar
いいえ、しかし、その行は実際には記録。 GNU Awkには、POSIX標準の一部ではない2つのツールがあります。
変数
RS
には複数の文字を含めることができます。この場合は正規表現です。この
RT
変数は、レコードの終端と一致するテキストの断片を保持します。
より:
$ awk 'BEGIN { RS = "(.)" } { print NF, RT }'
How now brown cow.
0 H
0 o
0 w
0
0 n
0 o
0 w
0
0 b
0 r
0 o
0 w
0 n
0
0 c
0 o
0 w
0 .
正規表現(.)
(すべての文字と一致)をレコード区切り文字として使用すると、フィールドがまったく含まれていない空のレコードが取得され、空のレコードを終了する文字はRT
GNU Awkで使用できます。
残念ながら、これは完全には機能しません。これを完全なプログラムに統合すると、次のようになります。
#!/bin/sh
trap 'stty $ttysave' EXIT INT TERM
ttysave=$(stty -g)
stty raw -echo
awk 'BEGIN { RS = "(.)" }
RT ~ /q/ { exit }
{ printf("[%s]", RT) }'
これは、前の文字を読み取るGawkのレコード区切り正規表現マシンに問題があることを示しています。たとえば、と入力してすぐに終了するには、q
これq
だけでは不十分です。現在のレコードを区切ってRT
設定できるとしても、Gawkはレコードを渡す前に文字を読み取るためにTTYから別のレコードをq
呼び出します。read
for
したがって、私たちはwhile
ループを繰り返したり、dd
ユーティリティを呼び出すなど、本当に見苦しい方法に頼る必要があります。
#!/bin/sh
trap 'stty $ttysave' EXIT INT TERM
ttysave=$(stty -g)
stty raw -echo
awk 'BEGIN { cmd = "dd bs=1 count=1 2> /dev/null"
for (;;)
{ cmd | getline ch
close(cmd)
if (ch == "q")
exit
printf("[%s]", ch) } }'