POSIXシェルのstdinから単一文字を読むことはできますか?

POSIXシェルのstdinから単一文字を読むことはできますか?

read -rそれはただPOSIXで指定;文字read -n NUMの読み取りにはNUM適していません。 stdinから与えられた数の文字を読み取った後に自動的に返す移植可能な方法はありますか?

私のユースケースは、次のプロンプトを印刷することです。

Do the thing? [y/n]

y可能であれば、入力後またはnユーザーがEnterキーを押さずに自動的にプログラムを継続するようにしたいと思います。

答え1

文字を読むとは、完全な文字が得られるまで一度に1バイトずつ読み込むことを意味します。

POSIXツールボックスを使用してバイトを読み取るにはdd bs=1 count=1

ただし、デバイスがicanonモードにあるとき(通常はデフォルト)、端末デバイスからデータを読み取ることはReturn(別名Enter)を押したときにのみ返されます。なぜなら、その前にターミナルデバイスドライバは、次のことができるAラインエディタフォームを実装しているからです。または、他の編集文字を使用して入力した内容を変更し、入力したBackspace内容は、編集中の行を送信するときにのみ(または+を使用して)読み取りアプリケーションで使用できるようになります。ReturnCtrlD

したがってksh、 'sread -n/Nまたはzsh'sread -kは stdin が端末デバイスであることを検出すると、デバイスをそのモードで取り出し、端末が送信されたicanonバイトを読み取ることができます。

これで、この内容は次のようkshになります。read -n n戻るnキャラクターに一行で、改行文字を読み込むと(文字-N n読み込み用n)、まだ停止します。bash、ksh93とは異なり、および-nIFSとバックスラッシュの処理を続けます-N

Mimics zsh's read -kor ksh93's read -N1or bash's IFS= read -rN 1、つまり POSIXly で stdin から 1 文字だけを読み取ることです。

readc() { # arg: <variable-name>
  if [ -t 0 ]; then
    # if stdin is a tty device, put it out of icanon, set min and
    # time to sane value, but don't otherwise touch other input or
    # or local settings (echo, isig, icrnl...). Take a backup of the
    # previous settings beforehand.
    saved_tty_settings=$(stty -g)
    stty -icanon min 1 time 0
  fi
  eval "$1="
  while
    # read one byte, using a work around for the fact that command
    # substitution strips trailing newline characters.
    c=$(dd bs=1 count=1 2> /dev/null; echo .)
    c=${c%.}

    # break out of the loop on empty input (eof) or if a full character
    # has been accumulated in the output variable (using "wc -m" to count
    # the number of characters).
    [ -n "$c" ] &&
      eval "$1=\${$1}"'$c
        [ "$(($(printf %s "${'"$1"'}" | wc -m)))" -eq 0 ]'; do
    continue
  done
  if [ -t 0 ]; then
    # restore settings saved earlier if stdin is a tty device.
    stty "$saved_tty_settings"
  fi
}

答え2

から引用この回答...これはbashで動作します。

echo -n "Is this a good question (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

答え3

ddのための他の解決策:

key=$(stty -icanon; dd ibs=1 count=1 2>/dev/null)

関連情報