Bash:读取输入可用(是否有任何键命中?) 问题:
我正在使用Bash5,有一个长时间运行的循环,需要偶尔检查用户可能碰到的各种键。我知道如何使用Bash:读取输入可用(是否有任何键命中?) 问题:,bash,unix,nonblocking,stty,Bash,Unix,Nonblocking,Stty,我正在使用Bash5,有一个长时间运行的循环,需要偶尔检查用户可能碰到的各种键。我知道如何使用stty-见下面我的答案-但它比应该的更难看 基本上,我正在寻找一种干净的方法来做到这一点: keyhit-p(){ 如果“read-n1将阻塞”;则 返回错误 其他的 返回真值 fi } 非解决方案:读取-t0 我已经阅读了bash手册,了解了read-t0。这并不是我想要的,也就是检测是否有任何输入可用。相反,只有当用户点击ENTER(完整的输入行)时,它才会返回true 例如: 为true时;做
stty
-见下面我的答案-但它比应该的更难看
基本上,我正在寻找一种干净的方法来做到这一点:
keyhit-p(){
如果“read-n1将阻塞”;则
返回错误
其他的
返回真值
fi
}
非解决方案:读取-t0
我已经阅读了bash
手册,了解了read-t0
。这并不是我想要的,也就是检测是否有任何输入可用。相反,只有当用户点击ENTER(完整的输入行)时,它才会返回true
例如:
为true时;做
如果读取-n1-t0;然后
echo“仅当您按enter键时,此选项才起作用”
打破
fi
完成
一个有效的答案,尽管很难看
虽然下面的工作,我希望有人有一个更好的答案
#!/bin/bash
# Reset terminal's stty to previous values on exit.
trap 'stty $(stty --save)' EXIT
keyhit-p() {
# Return true if input is available on stdin (any key has been hit).
local sttysave=$(stty --save)
stty -icanon min 1 time 0 # ⎫
read -t0 # ⎬ Ugly: This ought to be atomic so the
local status=$? # ⎪ terminal's stty is always restored.
stty $sttysave # ⎭
return $status
}
while true; do
echo -n .
if ! keyhit-p; then
continue
else
while keyhit-p; do
read -n1
echo Key: $REPLY
done
break
fi
done
这会在读取之前更改用户的终端设置(stty),并在读取之后尝试将其写回,但不会以原子方式进行。脚本可能会被中断,并使用户终端处于不正确的状态。我希望看到一个解决这个问题的答案,最好只使用bash内置的工具
更快、更难看的回答
上述例程中的另一个缺陷是,要想让一切正常,需要花费大量CPU时间。它需要调用一个外部程序(stty
)三次,以检查是否没有发生任何事情。叉子在环中可能很贵。如果不考虑正确性,我们可以得到一个运行速度快两个数量级(256×)的例程
与仅在读取测试期间更改为非规范模式不同,此脚本在开始时设置一次,并在脚本退出时使用异常处理程序撤消它
虽然我喜欢代码看起来更干净,但由于未处理挂起信号,原始版本的原子性缺陷更加严重。如果用户的shell是bash,则在进程挂起时启用icanon,但在进程预先固定时不禁用icanon。这使得read-t0
即使在按下键(而不是回车键)时也返回FALSE。其他用户shell可能不会像bash那样在^Z上启用icanon,但更糟糕的是,输入命令将不再像往常一样工作
此外,要求非规范模式始终保持打开状态可能会导致其他问题,因为脚本比这个简单的示例要长。没有记录非规范模式如何影响读取
和其他bash内置。它似乎在我的测试中起作用,但它会一直起作用吗?调用外部程序或被外部程序调用时,遇到问题的可能性会成倍增加。可能不会出现问题,但需要进行繁琐的测试。尽管这样做有意义,但请注意添加-n1
无助于通知bash我计划一次读取一个字符,而不是逐行读取。也许这应该是一个bash功能请求?丑陋是什么意思?“丑陋”如“将stty状态保存在变量中,然后希望在用户点击^C之前恢复它,使其终端损坏”。这应该是一个原子行动。我知道我可以捕获退出'stty sane'
,但这使得关于用户终端的假设并不总是有效的。
#!/bin/bash
# Reset terminal's stty to previous values on exit.
trap 'stty $(stty --save)' EXIT
# Set one character at a time input for the whole script.
stty -icanon min 1 time 0
while true; do
echo -n .
# We save time by presuming `read -t0` no longer waits for lines.
# This may cause problems and can be wrong, for example, with ^Z.
if ! read -t0; then
continue
else
while read -t0; do
read -n1
echo Key: $REPLY
done
break
fi
done