我可以从 POSIX shell 中的 stdin 读取单个字符吗?

我可以从 POSIX shell 中的 stdin 读取单个字符吗?

只不过read -r由 POSIX 指定; read -n NUM,用于读取NUM字符,不是。从 stdin 读取给定数量的字符后是否有一种可移植的方法自动返回?

我的用例是打印如下提示:

Do the thing? [y/n]

如果可能的话,我希望程序在输入y或后自动继续n,而不需要用户随后按 Enter 键。

答案1

读取一个字符意味着一次读取一个字节,直到获得完整的字符。

要使用 POSIX 工具箱读取一个字节,有dd bs=1 count=1.

但请注意,当该设备处于icanon模式(通常是默认情况下)时,从终端设备读取数据只会在您按下Return(又名Enter)时返回,因为在此之前,终端设备驱动程序实现了一种行编辑器形式,允许您使用或其他编辑字符来修改您输入的内容,并且仅当您提交正在编辑的行(使用或+ )Backspace时,您输入的内容才可用于阅读应用程序。ReturnCtrlD

因此,ksh'sread -n/Nzsh's read -k,当它们检测到 stdin 是终端设备时,将该设备退出该icanon模式,以便终端发送字节后就可以读取它们。

现在请注意,这ksh只是read -n n读取向上n字符从单行,当读取换行符时它仍然停止(用于-N n读取n字符)。bash,与 ksh93 相反,仍然对-n和进行 IFS 和反斜杠处理-N

模仿zsh'sread -kksh93'sread -N1bash's IFS= read -rN 1,即从 stdin 读取一个且仅一个字符,POSIXly:

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)

相关内容