shell脚本响应keypress

时间:2014-06-03 13:07:37

标签: bash shell input keypress

我有一个shell脚本,基本上说的是

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

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

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

5 个答案:

答案 0 :(得分:21)

read -rsn1

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

答案 1 :(得分:10)

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

#!/bin/bash

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

答案 2 :(得分: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。

答案 3 :(得分: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)然后返回" head"的输入。 (内置shell),带有-c1选项,返回标准输入的一个字节。如果你不包括" stty -icanon"然后脚本回显按下的键的字母,然后等待RETURN(不是我们想要的)。两者都是"头和#34;和" 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脚本具有交互性。我希望这会有所帮助。

答案 4 :(得分: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