我使用的 shell 是 GNU bash,版本 6.2.15(1)-release(i686-pc-linux-gnu)。
我总是使用以下代码来停止脚本运行:
read -p "type RET to continue"
我有一个名为的 bash 脚本
/tmp/WEx_tst01.sh
该脚本包含两行
showkey -k
read -p "type RET to continue"
权限由
chmod a+x /tmp/WEx_tst01.sh
无论脚本是否以前缀“.”作为来源,行为都是相同的。因此,我仅描述以前缀“.”作为来源的示例。
情况1:
脚本直接被调用
/tmp/WEx_tst01.sh
情况 2:
文件名通过 shell 内置的“bind”映射到键序列 '''Alt-R''' = '''Mr''',例如
bind -x '"\er":"/tmp/WEx_tst01.sh"'
经过
bind -X
正确的绑定已被批准。
结果:
情况 1 正常工作,即如果我按下 RETURN = '''Enter''' 键,暂停的脚本将继续运行。
情况 2 不起作用。脚本无法继续。好像我没有按 RETURN = '''Enter''' 键。好像读取函数不会对传入的字符做出反应。
事实上,其他测试证明读取函数会忽略传入的字符但继续等待输入。
然而,脚本接收到了输入的 RETURN = '''Enter''' 键。这得到了第一行 showkey 的认可。
请问这是什么原因,我该如何克服?
问候
答案1
通过在脚本中包含stty -a
该选项,我发现当 Bash 从绑定执行它时,终端配置为-icanon
、-icrnl
和-echo
。通常,当从命令行执行脚本时,这些选项是icanon
、icrnl
和echo
。
read
我在等待输入时还读取了设置,只是为了确保 shell 不会单独为 重新配置终端read
。我在等待时通过</dev/pts/… stty -a
在另一个控制台(…
用正确的值替换)中执行来做到这一点read
。结果是一样的。
-icrnl
是罪魁祸首。请参阅man 1 stty
。此设置负责将回车符 (CR) 转换为换行符 (NL)。
传统上,终端和终端仿真器在 时发送 CR 。它与您按+Enter获得的字符相同。使用 时,每个 CR 都由 tty 的行规程转换为 NL。Shell 和其他工具通常希望在行末出现 NL,并且转换正是它们需要做出反应的内容,正如您所期望的那样。特别是Bash 的内置功能需要它。CtrlMicrln
Enterread
当您从绑定运行脚本时,不会发生翻译(-icrnl
)。您的脚本仍会对Ctrl+做出反应J(尝试一下),因为它发送了正确的字符(NL),不需要翻译。
有点令人惊讶的是,当 Bash 允许您在提示符后在命令行中键入命令时,icrnl
被禁用(即)。和也被禁用。 Bash 本身(更具体地说:Bash 使用的 Readline 库)解释 CR(以及一些其他东西)并回显字符;它不依赖于行规则。交互式 Bash 在启动前台命令(例如或,或)时将终端配置为(可能还有其他设置) ;当需要以交互方式读取下一个命令时,交互式 Bash 会将终端自行配置回。-icrnl
icanon
echo
icanon icrnl echo
cat
/tmp/WEx_tst01.sh
read
-icanon -icrnl -echo
显然,Bash 在由于绑定而调用您的脚本之前不会重新配置终端。remain -icanon -icrnl -echo
,而read
在您的脚本中则期望icanon icrnl echo
。
为了使您的脚本对做出反应,只需在脚本中Enter启用即可:icrnl
read
stty icrnl # you may also want echo: stty icrnl echo
read …
或者告诉read
使用 Readline。Readline 将相应地配置终端。请注意,脚本必须由 Bash 解释,因此您需要正确的 shebang:
#!/bin/bash
read -ep "type RET to continue"
需要 shebang 不仅仅是因为-e
。-p
您使用的 也是不可移植的。 即使您的原始脚本需要 shebang,通常它也不能由随机的 POSIX 兼容 shell 执行。
没有任何事发生,无论如何都会使用 Bash,至少从 Bash 开始(包括“从 Bash 中的绑定”开始)正确的做法是使用shebang任何设计用于执行的脚本(而不是源脚本)。
如果没有 shebang,在我的 Bash 5.1.4 中执行绑定中的脚本(甚至只包含注释的脚本)会导致icanon
之后设置不正确。这可能是一个错误。正确的 shebang 有帮助,但我不知道为什么。
请注意,Bash 在击键后执行脚本时不仅不会为脚本准备终端。Bash 在脚本退出时也不会恢复正确的设置。stty icrnl
我们使用的设置将保留(尽管它可能不引人注意;它将在下一次左右修复)。您可以在+Enter之前保存设置,然后在之后恢复它们。示例:stty
read
#!/bin/bash
settings="$(stty -g)"
stty icrnl
read -p "type RET to continue"
stty "$settings"
当上述脚本由于按键而运行时,如果我在过程中按下Ctrl+,那么最后一个脚本将不会被执行。Cread
stty
有时我在+icanon
之后得到结果,尽管当时是在 期间;这似乎在某种程度上取决于脚本后面的内容(并且由于+仍未执行),结果似乎不一致,我不会进一步调查。我在+期间也得到结果。CtrlC-icanon
read
CtrlCicanon
CtrlCread -e
icanon
当 Bash 返回提示符时是有害的,因为它严重削弱了我们编辑命令行的能力。
一个解决方法可能是告诉生产线纪律不要将Ctrl+C视为特殊情况:
#!/bin/bash
settings="$(stty -g)"
stty icrnl intr ''
read -p "type RET to continue"
echo # so the next prompt will be in the next line
stty "$settings"
我怀疑 bind -x
并非设计用于运行从终端读取和/或更改其设置的命令,因此出现问题。