Bash脚本编程-读取包括特殊键(回车和空格)在内的单个按键

10
我不确定我应该把这个问题放在stackoverflow还是unix.stackexchange上,但我在这里找到了一些类似问题(链接)(链接),所以这里就介绍一下。
我正在尝试创建一个脚本,由.bashrc调用,允许我根据单个按键选择两个选项中的一个。通常情况下这并不难,但我想要对应于两个选项的两个按键分别是空格和回车。
这是我目前已经得到的:
#!/bin/bash

SELECT=""
while [[ "$SELECT" != $'\x0a' && "$SELECT" != $'\x20' ]]; do
    echo "Select session type:"
    echo "Press <Enter> to do foo"
    echo "Press <Space> to do bar"
    read -s -N 1 SELECT
    echo "Debug/$SELECT/${#SELECT}"
    [[ "$SELECT" == $'\x0a' ]] && echo "enter" # do foo
    [[ "$SELECT" == $'\x20' ]] && echo "space" # do bar
done

以下是我按下回车、空格、退格和x时得到的输出结果:
:~$ bin/sessionSelect.sh
Select session type:
Press <Enter> to start/resume a screen session
Press <Space> for a regular ssh session
Debug//0
Select session type:
Press <Enter> to start/resume a screen session
Press <Space> for a regular ssh session
Debug//0
Select session type:
Press <Enter> to start/resume a screen session
Press <Space> for a regular ssh session
Debug//1
Select session type:
Press <Enter> to start/resume a screen session
Press <Space> for a regular ssh session
Debug/x/1

因此,无论是回车还是空格都会导致一个空的SELECT。无法区分这两者。我尝试在读取选项中添加“-d 'D'”,但没有帮助。也许有人可以指点一下方向。

顺便提一下,bash 版本为 4.2。


相关 https://unix.stackexchange.com/questions/179191/bashscript-to-detect-right-arrow-key-being-pressed - Ciro Santilli OurBigBook.com
3个回答

7
尝试将读取分隔符设置为空字符串,然后检查内置的 $REPLY 变量。
read -d'' -s -n1

由于某些原因,我无法使用变量来使其工作。

这一段代码非常好用,即使没有使用-d改变分隔符。回车和空格都能被正确识别。 - a.peganz
1
我们可能有不同版本的read。如果我省略了-d'',我就无法获取回车键。 - Cole Tierney
我刚刚发现我们分别使用-n和-N。这一定是因为,当使用-N指定长度时,换行符不会被特殊处理。 - a.peganz
我本来想用-N选项进行测试,但是我的read函数没有这个选项。 - Cole Tierney

5
#!/bin/bash
SELECT=""
# prevent parsing of the input line
IFS=''
while [[ "$SELECT" != $'\x0a' && "$SELECT" != $'\x20' ]]; do
  echo "Select session type:"
  echo "Press <Enter> to do foo"
  echo "Press <Space> to do bar"
  read -s -N 1 SELECT
  echo "Debug/$SELECT/${#SELECT}"
  [[ "$SELECT" == $'\x0a' ]] && echo "enter" # do foo
  [[ "$SELECT" == $'\x20' ]] && echo "space" # do bar
done

IFS='' 不是必需的。read -N 会原样返回输入,忽略 IFS(请参阅 man 手册)。我已经测试过了,没有使用 IFS='',也能正常工作。非常好的答案!我用它来实现了自己的“--More--”版本。 - Fonic
@Maxxim 这可能是在新版本的Bash中。至少对于带有bash-4.2.46(2)的Centos 7.6,需要使用IFS='' - Andrey
你说得对。这个更改是在 Bash 4.4 中引入的。在 read -N ncharsman 页面中,添加了以下内容:_[...] 结果不会根据 IFS 中的字符进行分割;意图是将变量正好分配为读取的字符(除了反斜杠;请参见下面的 -r 选项)。_ 你能编辑一下你的答案并指出这一点吗?这会帮助其他人。 - Fonic
基于这个(非官方的)变更列表,很难确定给定的bash实例的行为方式(IFS处理中有许多错误)。此外,不要忘记回溯 - Andrey

3
这里有几点关于read的事项是相关的:
  • 它读取一行
  • 该行被分成字段,就像单词拆分一样
由于你正在读取一个字符,这意味着输入Enter将导致一个空变量。
此外,默认的单词拆分规则会导致输入Space也会导致一个空变量。好消息是,你可以通过设置IFS来处理这个部分。
将你的read语句更改为:
IFS= read -s -n 1 SELECT

输入 Enter 键时,期望接收一个空字符串而不是 $'\x0a'


这个解决方案同样有效,而且只为read命令设置IFS基本上是一个干净的解决方案。我仍然将putnamhill的方案标记为正确的,因为它不会通过更改IFS产生意外的副作用。但我会点赞的!顺便说一下,使用-N而不是-n时,当输入回车时,SELECT不是空的,实际上包含了预期的值。 - a.peganz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接