shell脚本响应keypress

问题描述 投票:14回答:5

我有一个shell脚本,基本上就是这样的

while true; do
    read -r input
    if ["$input" = "a"]; then 
        echo "hello world"           
    fi
done

这一切都很好,很好,但我只是意识到必须点击ENTER在这种情况下提出了一个严重的问题。我需要的是脚本在按下键时响应,而不必按Enter键。

有没有办法在shell脚本中实现此功能?

bash shell input keypress
5个回答
20
投票
read -rsn1

期待只有一个字母(并且不要等待提交)并保持沉默(不要写回那封信)。


9
投票

所以最后的工作片段如下:

#!/bin/bash

while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
    echo "hello world"
fi
done

4
投票

另一种方式,以非阻塞的方式(不确定它是否是你想要的)。您可以使用stty将最小读取时间设置为0.(如果之后没有使用stty sane,则有点危险)

stty -icanon time 0 min 0

然后就像正常一样运行循环。不需要-r。

while true; do
    read input

    if ["$input" = "a"]; then 
        echo "hello world"           
    fi
done

重要!完成非阻塞后,您必须记住使用stty设置恢复正​​常

stty sane

如果你没有,你将无法在终端上看到任何东西,它似乎会挂起。

你可能想要为ctrl-C包含一个陷阱,就好像在你将stty恢复到正常状态之前退出脚本一样,你将无法看到你键入的任何东西,并且它将显示终端已经冻结。

trap control_c SIGINT

control_c()
{
    stty sane
}

P.S此外,您可能希望在脚本中放置一个sleep语句,这样就不会耗尽所有CPU,因为这样可以尽可能快地运行。

sleep 0.1

P.S.S似乎悬挂的问题只有在我使用-echo的时候,因为我以前可能不需要。我将把它留在答案中,因为将stty重置为默认值以避免将来出现问题仍然很好。如果你不想要你输入的内容出现在屏幕上,你可以使用-echo。


2
投票

你可以使用这个getkey函数:

getkey() {
    old_tty_settings=$(stty -g)   # Save old settings.
    stty -icanon
    Keypress=$(head -c1)
    stty "$old_tty_settings"      # Restore old settings.
}

它暂时关闭终端设置(stty -icanon)中的“规范模式”,然后使用-c1选项返回“head”(内置shell)的输入,该选项返回标准输入的一个字节。如果您不包含“stty -icanon”,则脚本会回显按下的键的字母,然后等待RETURN(不是我们想要的)。 “head”和“stty”都是shell内置命令。收到按键后保存和恢复旧的终端设置非常重要。

然后,getkey()可以与“case / esac”语句结合使用,以便从条目列表中进行交互式一键选择:示例:

case $Keypress in
   [Rr]*) Command response for "r" key ;;
   [Ww]*) Command response for "w" key ;;
   [Qq]*) Quit or escape command ;;  
esac

这个getkey()/case-esac组合可用于使许多shell脚本交互。我希望这有帮助。


1
投票

我有办法在我的项目中执行此操作:https://sourceforge.net/p/playshell/code/ci/master/tree/source/keys.sh

每次调用key_readonce时它都会读取一个键。对于特殊键,运行一个特殊的解析循环也可以解析它们。

这是它的关键部分:

if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
    KEY[0]=$K

    if [[ $K == $'\e' ]]; then
        if [[ BASH_VERSINFO -ge 4 ]]; then
            T=(-t 0.05)
        else
            T=(-t 1)
        fi

        if read -rn 1 -d '' "${T[@]}" "${S[@]}" K; then
            case "$K" in
            \[)
                KEY[1]=$K

                local -i I=2

                while
                    read -rn 1 -d '' "${T[@]}" "${S[@]}" "KEY[$I]" && \
                    [[ ${KEY[I]} != [[:upper:]~] ]]
                do
                    (( ++I ))
                done
                ;;
            O)
                KEY[1]=$K
                read -rn 1 -d '' "${T[@]}" 'KEY[2]'
                ;;
            [[:print:]]|$'\t'|$'\e')
                KEY[1]=$K
                ;;
            *)
                __V1=$K
                ;;
            esac
        fi
    fi

    utils_implode KEY __V0
© www.soinside.com 2019 - 2024. All rights reserved.