【问题标题】:shell script respond to keypressshell脚本响应按键
【发布时间】:2014-07-23 20:10:42
【问题描述】:

我有一个 shell 脚本,它基本上说类似

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

这一切都很好,很好,但我刚刚意识到在这种情况下必须按 ENTER 会带来一个严重的问题。我需要的是脚本在按下一个键时做出响应,而不必按回车键。

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

【问题讨论】:

  • 如果这是您的真实代码,请注意您在if 条件中错过了[] 周围的空格。
  • 不,这不是我真正的代码。但感谢您的提示
  • 脚本的应用程序正在显示中。以前的控制面板是用鼠标做的,点击作为操作机制。但他们想为数据存储添加一个投币式操作。原来的电脑死机了,一直在运行 windows 98,没有任何硬件或软件可以在新的 windows 操作系统上运行。所以我推导出了各个操作,并重新连接了一个键盘来提供 IO 功能。但是当我回家编写脚本的其余部分时,远程登录,它似乎不起作用。展示的观众是儿童。随机 IO 存在很大风险

标签: bash shell input keypress


【解决方案1】:
read -rsn1

只期待一封信(不要等待提交)并保持沉默(不要回信)。

【讨论】:

  • 伙计,我昨晚试过了,但它似乎不起作用。但延迟可能是一个问题。我再试一次。
  • @j0h 你想继续循环吗,因为这会阻塞直到输入一些东西?
  • 我想让它问用户:在完成每一步之后,一遍又一遍地问他们为什么要这样做。不幸的是,我还在家里,在 Windows 电脑上。
  • 如果您需要检查输入是否可用,请尝试添加-t 0.01 等待几分之一秒,如果没有按键等待则放弃。如果您想要一个非常紧密的循环,较小的数字仍然可能完全没问题。非正式实验表明 0.00001 有效,但 0.000001 太小了。
  • 我用这个命令创建了一个自我更新的状态信息脚本,它可以接受按键输入来改变显示。 read -rsn1 -t 5 key$key读取一键退出,否则等待5秒重新渲染状态信息。非常方便,谢谢。
【解决方案2】:

所以最终的工作 sn-p 如下:

#!/bin/bash

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

【讨论】:

    【解决方案3】:

    另一种方式,以非阻塞方式(不确定它是否是你想要的)。您可以使用 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。

    【讨论】:

      【解决方案4】:

      你可以使用这个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 脚本交互。我希望这会有所帮助。

      【讨论】:

      • getkey() 的可用性各不相同。
      【解决方案5】:

      我有办法在我的项目中做到这一点: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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-06
        相关资源
        最近更新 更多