Bourne shell 是否有等效于 `read -n` 的命令?

Bourne shell 是否有等效于 `read -n` 的命令?

有没有办法让我的脚本等待用户按下任意键,然后继续执行而无需用户按下任何键Enter?我想让它在 Bourne shell ( sh) 中工作,而不是在 Bash 中工作。

答案1

初步说明

如今sh不一定是传统的 Bourne shell. 现在sh是一个支持至少POSIX 所需的特性(假设正确实现)。POSIX 指定sh公用事业Shell 命令语言

我假设您希望您的代码能够在 POSIX shell 中运行并且具有可移植性。


问题

dd ibs=1 count=…是一种读取精确字节数的 POSIX 方式。它似乎是唯一可以可靠地完成这项工作的便携式命令行实用程序。但dd读取字节,而read -n在 Bash 中读取人物POSIX 允许在除以下语言环境中使用多字节字符:POSIX。即使你用 运行整个脚本LC_ALL=POSIX,终端(终端仿真器)仍可能在某个按键时生成多字节序列。这样的序列可能不是多字节字符;它可能是转义序列(例如F1,另见这个答案)。

在您读取一个字节之后,其余部分将保留,并且它将被稍后尝试从终​​端读取的任何内容读取(它可能是脚本的一部分或运行脚本的交互式 shell 的一部分)。

此外,如果你单独跑步dd ibs=1 count=1,那么你很可能会发现,dd除非你超过{MAX_INPUT}或者{MAX_CANON},或按EnterCtrl+D(如果count超过1,如果您想提供更少的字节,那么您可能需要按Ctrl+D 两次)。

为了解决这些问题,你需要非规范模式。 使用stty -icanon。总体来说,从stty raw看起来是个好主意。


代码

以下示例是概念验证。因为它操纵终端的线路设置并从终端读取一定不将其粘贴到交互式 shell 中,这不起作用. 将其粘贴到文件中并运行该文件。

#!/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+CCtrl+Z也可以是“任意键”,而不是分别发送SIGINTSIGSTOP。但是,解释脚本的 shell 可能会从其他地方收到信号,因此一般情况下,它可能会在到达 之前退出stty "$saveterm";因此,它可能会使终端处于不适合交互使用的状态。无论如何,您可能希望捕获相关信号并恢复终端的初始状态。

  • 这是真的应该用双引号引起变量一般来说。这里$saveterm故意不加引号,因为stty -g生成的输出未指定。的实现stty允许生成带空格的输出,然后希望 shell 将其拆分并传递多个参数。指定的是一条约束,即stty -g不加引号时的输出必须是安全的,它不能在 shell 中触发字扩展。为了便于移植,不加引号$saveterm更好。

    编辑:这是 POSIX 规范的一个缺陷。以上代码已修复。

  • 要知道读取了哪个字节,您需要将 的输出保存dd到稍后将检查的常规文件 ( >some_file) 或变量 ( variable="$(dd …)")。但是:

    • 终端能够生成空字节(通常使用Ctrl+ @),但大多数(所有?)实现都sh无法将空字节存储在变量中。存储在文件中是可以的,只要您无需将其读入变量即可检查/操作其内容。
    • dd从上面的代码中只能得到一个字节,可能不足以区分字符或键。另一方面,单个字节应该足以区分其他Q任何东西,因此Press Q to quit or any other key to continue.似乎P - proceed; B - back; Q - quit; H - help可以在不分析更多字节的情况下实现。

那么更多的字节呢?

上面的代码对于“按任意键”来说应该没问题。 的真正等价版本read -n应该一次读取一个字节并解码序列,直到获得所需的人物。我不会尝试在这里建造它。

相关内容