Bourne シェルに `read -n` と同等のコマンドはありますか?

Bourne シェルに `read -n` と同等のコマンドはありますか?

スクリプトを、ユーザーが任意のキーを押すまで待機させ、その後は を押さなくても続行させる方法はありますか? これをBash ではなくEnterBourne シェル ( ) で動作させたいと思います。sh

答え1

予備的注釈

今でsh必ずしもレガシーBourneシェルではない今ではshサポートしているシェルです少なくともPOSIXで要求される機能(正しい実装を前提としています)。POSIXでは、shユーティリティそしてそのシェルコマンド言語

コードを POSIX シェルで動作させ、一般的に移植可能にしたいと考えていると思います。


問題

dd ibs=1 count=…正確なバイト数を読み取るPOSIXの方法である確実に機能する唯一のポータブルコマンドラインユーティリティのようです。しかし、ddバイトを読み取りread -nますが、Bashでは文字POSIXでは、以下のロケール以外でもマルチバイト文字が使用可能です。POSIXスクリプト全体を で実行したとしてもLC_ALL=POSIX、ターミナル(ターミナルエミュレーター)は、単一のキーストロークでマルチバイトシーケンスを生成する場合があります。このようなシーケンスはマルチバイト文字ではない可能性があり、エスケープシーケンス(例えばF1、また参照この答え)。

1 バイトだけ読み取ると、残りは残り、後でターミナルから読み取ろうとするすべてのもの (スクリプトの一部、またはスクリプトを実行する対話型シェル) によって読み取られます。

さらに、あなたが単独で実行する場合、あなたが超過するまでdd ibs=1 count=1何も到達しないことに気付く可能性が高いですdd{MAX_INPUT}または{MAX_CANON}、またはを押すEnterか、Ctrl+Dcount(そしてより大きい場合1、より少ないバイト数を指定したい場合はCtrl+ を押す必要があるかもしれません。D 2回)。

これらの問題に対処するには、非正規モード。 使用stty -icanon一般的に、から始めるのがstty raw良い考えのように思えます。


コード

次の例は概念実証です。端末の回線設定を操作し、端末から読み取るためあなたしてはならないインタラクティブシェルに貼り付けても動作しませんそれをファイルに貼り付けて実行します。

#!/bin/sh

echo "Press any key to continue."

saveterm="$(stty -g)"                     # save terminal state
stty raw
stty -echo -icanon min 1 time 0           # prepare to read one byte
dd ibs=1 count=1 >/dev/null 2>/dev/null   # read one byte
stty -icanon min 0 time 0                 # prepare to read lefotvers
while read none; do :; done               # read leftovers
stty "$saveterm"                          # restore terminal state

ノート

  • スクリプトはstdinが端末であるかどうかをチェックしませんが、一般的にはチェックする必要があります([ -t 0 ])。

  • 正常な設定では、修飾キー (例Shift) を単独で押すと、端末から読み取ったものに入力が送信されません。そのため、コードではそのようなキーを「任意のキー」として登録しません。

  • 「残り物を読む」というトリックは、この答え

  • stty raw Ctrl+CまたはCtrl+のおかげで、それぞれまたは をZ送信する代わりに、「任意のキー」にもなります。それでも、スクリプトを解釈するシェルは他の場所からシグナルを取得する可能性があるため、一般的には に到達する前に終了する可能性があります。そのため、対話型の使用に適さない状態で端末が残される可能性があります。関連するシグナルをトラップして、とにかく端末の初期状態を復元することをお勧めします。SIGINTSIGSTOPstty "$saveterm"

  • それは本当です変数は二重引用符で囲む必要がある一般的には、は出力が不特定になる$savetermため、ここでは意図的に引用符で囲んでいません。 の実装では、スペースを含む出力を生成し、後でシェルがそれを分割して複数の引数を渡すことが許可されています。 指定されているのは、 の出力が引用符で囲まれていない場合でも安全でなければならないという制約であり、シェルで単語の展開をトリガーしてはなりません。移植性のためには、引用符で囲まない方がよいでしょう。stty -gsttystty -g$saveterm

    編集:それはPOSIX仕様の欠陥でした上記のコードは修正されました。

  • どのバイトが読み取られたかを知るには、 の出力を、後で調べるdd通常のファイル ( ) または変数 ( ) に保存する必要があります。ただし、>some_filevariable="$(dd …)"

    • 端末はヌルバイトを生成できます (通常はCtrl+を使用@) が、の実装のほとんど (すべて?)shでは、変数にヌルバイトを保存できません。変数に読み込まずにその内容を調べたり操作したりできる場合は、ファイルに保存しても問題ありません。
    • ddQ上記のコードからは 1 バイトしか得られないので、文字やキーを特定するには不十分かもしれません。一方、1 バイトでも他のものと区別するには十分なので、それ以上のバイトを分析せずに実装することは可能と思われますPress Q to quit or any other key to continue.P - proceed; B - back; Q - quit; H - help

バイト数を増やす場合はどうでしょうか?

上記は「任意のキーを押す」には適しているはずです。実際の同等のものは、read -n一度に1バイトを読み取り、必要な数のキーを取得するまでシーケンスをデコードする必要があります。文字。ここでそれを作ろうとはしません。

関連情報