POSIXシェルとawkが入力文字を1行ずつ読みずに文字ごとに読み取るようにするにはどうすればよいですか?

POSIXシェルとawkが入力文字を1行ずつ読みずに文字ごとに読み取るようにするにはどうすればよいですか?

POSIXシェルとawkが入力文字を1行ずつ読みずに文字ごとに読み取るようにするにはどうすればよいですか?

ローマ字からガーナへの音訳アプリケーションを作成していますが、awkへの入力を文字ごとにすぐに処理したいと思います。

awkが文字を処理する前にreturnまたはenterを押すことなくこれを行う正しい方法がわかりません。

答え1

シェルスクリプトでこのツールを使用してTTYステータスを操作できますstty

まず、stty -g現在の状態を表す文字列を生成します。他の作業を実行する前に、この出力をキャプチャしてどこかに保存してください。後で、この文字列をsttyTTY設定を復元する唯一の引数として渡すことができます。文字列には引用符は必要ありません。 POSIX標準では、stty -gシェルスクリプトで引用符で囲む必要のない表現を生成する必要があります。

stty raw一度に1文字ずつ入力できる生モードに入る方法です。

savetty=$(stty -g)
stty raw
...
stty $savetty

スクリプトがどこかで終了または中断された場合でも、設定を復元するtrapハンドラを設定するには、このコマンドを使用することをお勧めします。tty

sttyそれでは、このダンスをAwkコードでラップしているとしましょう。一般性を失うことなく、Awkの外でこれを行いましょう。 awkに一度に1文字ずつ読み込むにはどうすればよいですか?

awkは暗黙的なスキャン戦略または演算子を使用して行を読み取ることができますgetlinegetcharいいえ、しかし、その行は実際には記録。 GNU Awkには、POSIX標準の一部ではない2つのツールがあります。

  1. 変数RSには複数の文字を含めることができます。この場合は正規表現です。

  2. この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 .
 

正規表現(.)(すべての文字と一致)をレコード区切り文字として使用すると、フィールドがまったく含まれていない空のレコードが取得され、空のレコードを終了する文字はRTGNU 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) } }'

関連情報