我在 bash shell 脚本中运行一个循环,直到按下某个键为止(Ctrl-C 对我来说总是很残酷)。尽管我使用 -s 选项read
,但单个字符显示在终端中。为什么 -s 选项read
没有达到我的预期!
until read -s -n 1 -t 0.01; do
echo -n "."
sleep .5 # actual script code goes here
done
echo
答案1
我很确定这是因为按键是在sleep .5
运行时按下的,而不是在运行时按下read
的。
更详细地说:当它运行时,read -s -n 1 -t 0.01
将终端置于 noecho 模式...0.01 秒,然后超时并重新打开 echo。如果您在那 0.01 秒内键入了一个字符,则该字符不会得到回显,但您可能错过了该窗口。相反,您在sleep .5
运行时输入它,因此它会回显。然后半秒后read
再次运行,关闭回显(太晚了!),从预输入缓冲区中读取字符,最后重新打开回显。
(注意:该echo
命令和 echo 终端模式没有直接联系,除了它们都涉及打印到终端的内容之外。)
解决方案:不要指望read -s
关闭回声,而是stty -echo
在开始时使用一次(并stty echo
在最后将其重新打开)。正如比利叔叔在评论中指出的那样,trap
如果脚本由于某种原因异常退出(例如 Control-C),添加 a 以确保终端设置恢复正常也是明智的:
trap 'stty echo' EXIT # If script exits abnormally, fix terminal
stty -echo # turn off echo
until read -n 1 -t 0.01; do
echo -n "."
sleep .5
done
echo
stty echo # turn echo back on
(您可以trap
随后删除它,但这并不重要 - 将终端设置为应该的状态无论如何都是一个非常安全的操作。)
编辑:正如 ilkkachu 在评论中指出的那样,最好完全删除sleep
,并使用更长的超时read
- 这样当您按下某个键时,它不会等待 0.5 秒才注意到你的按键。您还可以使用更长的超时。我仍然会继续使用stty -echo
关闭回声,而不是read -s
每次都单独关闭它:
trap 'stty echo' EXIT
stty -echo # turn off echo
until read -n 1 -t 3600; do # 1 hour timeout, 'cause why not
echo -n "."
done
echo
stty echo # turn echo back on
如果您不需要循环,您可以完全删除它(在这种情况下read -s
应该可以正常工作):
read -s -n 1