Bash:如何在按下任意键的情况下结束无限循环?

Bash:如何在按下任意键的情况下结束无限循环?,bash,while-loop,Bash,While Loop,我需要写一个无限循环,当按下任何键时,循环停止 不幸的是,这个只有在按键时才会循环 好主意吗 #!/bin/bash count=0 while : ; do # dummy action echo -n "$a " let "a+=1" # detect any key press read -n 1 keypress echo $keypress done echo "Thanks for using this script." ex

我需要写一个无限循环,当按下任何键时,循环停止

不幸的是,这个只有在按键时才会循环

好主意吗

#!/bin/bash

count=0
while : ; do

    # dummy action
    echo -n "$a "
    let "a+=1"

    # detect any key  press
    read -n 1 keypress
    echo $keypress

done
echo "Thanks for using this script."
exit 0

通常我不介意用一个简单的CTRL-C来打破bash无限循环。这是终止
尾部的传统方法,例如-f

read
有许多字符参数
-n
和一个可以使用的超时参数
-t

发件人:

-nnchars 读取在读取nchars字符后返回,而不是等待完整的输入行,但如果在分隔符之前读取的nchars字符少于nchars,则接受分隔符

-t超时

如果在超时秒内未读取完整的输入行(或指定数量的字符),则导致读取超时并返回失败。超时可以是小数点后带有小数部分的十进制数。仅当读取是从终端、管道或其他特殊文件读取输入时,此选项才有效;从常规文件读取时,它没有任何效果。如果读取超时,read会将读取的任何部分输入保存到指定的变量名中。如果timeout为0,则read立即返回,而不尝试读取任何数据。如果输入在指定的文件描述符上可用,则退出状态为0,否则为非零。如果超过超时,则退出状态大于128

但是,读取内置使用具有自己设置的终端。因此,正如其他答案所指出的,我们需要使用
stty
设置终端的标志

#!/bin/bash
old_tty=$(stty --save)

# Minimum required changes to terminal.  Add -echo to avoid output to screen.
stty -icanon min 0;

while true ; do
    if read -t 0; then # Input ready
        read -n 1 char
        echo -e "\nRead: ${char}\n"
        break
    else # No input
        echo -n '.'
        sleep 1
    fi       
done

stty $old_tty

您需要将标准输入置于非阻塞模式。下面是一个有效的示例:

#!/bin/bash

if [ -t 0 ]; then
  SAVED_STTY="`stty --save`"
  stty -echo -icanon -icrnl time 0 min 0
fi

count=0
keypress=''
while [ "x$keypress" = "x" ]; do
  let count+=1
  echo -ne $count'\r'
  keypress="`cat -v`"
done

if [ -t 0 ]; then stty "$SAVED_STTY"; fi

echo "You pressed '$keypress' after $count loop iterations"
echo "Thanks for using this script."
exit 0
编辑2014/12/09:
-icrnl
标记添加到
stty
以正确捕捉返回键,使用
cat-v
而不是
读取
以捕捉空格

如果数据传输足够快,则
cat
可能读取多个字符;如果不是所需的行为,则将
cat-v
替换为
dd bs=1 count=1 status=none | cat-v


编辑2019/09/05:使用
stty--save
恢复TTY设置。

这里是另一个解决方案。它适用于任何按下的键,包括空格、回车、箭头等

在bash中测试的原始解决方案:

IFS=''
if [ -t 0 ]; then stty -echo -icanon raw time 0 min 0; fi
while [ -z "$key" ]; do
    read key
done
if [ -t 0 ]; then stty sane; fi
在bash和dash中测试的改进解决方案:

if [ -t 0 ]; then
   old_tty=$(stty --save)
   stty raw -echo min 0
fi
while
   IFS= read -r REPLY
   [ -z "$REPLY" ]
do :; done
if [ -t 0 ]; then stty "$old_tty"; fi
在bash中,您甚至可以省略
read
命令的
REPLY
变量,因为它是那里的默认变量。

我找到了
era
的帖子,并将其改写为这种非常通用的格式:

# stuff before main function
printf "INIT\n\n"; sleep 2

INIT(){
  starting="MAIN loop starting"; ending="MAIN loop success"
  runMAIN=1; i=1; echo "0"
}; INIT

# exit script when MAIN is done, if ever (in this case counting out 4 seconds)
exitScript(){
    trap - SIGINT SIGTERM SIGTERM # clear the trap
    kill -- -$$ # Send SIGTERM to child/sub processes
    kill $( jobs -p ) # kill any remaining processes
}; trap exitScript SIGINT SIGTERM # set trap

MAIN(){
  echo "$starting"
  sleep 1

  echo "$i"; let "i++"
  if (($i > 4)); then printf "\nexiting\n"; exitScript; fi

  echo "$ending"; echo
}

# main loop running in subshell due to the '&'' after 'done'
{ while ((runMAIN)); do
  if ! MAIN; then runMain=0; fi
done; } &

# --------------------------------------------------
tput smso
# echo "Press any key to return \c"
tput rmso
oldstty=`stty -g`
stty -icanon -echo min 1 time 0
dd bs=1 count=1 >/dev/null 2>&1
stty "$oldstty"
# --------------------------------------------------

# everything after this point will occur after user inputs any key
printf "\nYou pressed a key!\n\nGoodbye!\n"
运行

Pure:无人参与的用户输入循环 我不用和stty玩就做到了:

loop=true
while $loop; do
    trapKey=
    if IFS= read -d '' -rsn 1 -t .002 str; then
        while IFS= read -d '' -rsn 1 -t .002 chr; do
            str+="$chr"
        done
        case $str in
            $'\E[A') trapKey=UP    ;;
            $'\E[B') trapKey=DOWN  ;;
            $'\E[C') trapKey=RIGHT ;;
            $'\E[D') trapKey=LEFT  ;;
            q | $'\E') loop=false  ;;
        esac
    fi
    if [ "$trapKey" ] ;then
        printf "\nDoing something with '%s'.\n" $trapKey
    fi
    echo -n .
done
这将

  • 占用空间非常小的环路(最大2毫秒)
  • 对光标左键、光标右键、光标向上键和光标向下键做出反应
  • 使用Escape或q键退出循环

我知道这有点左字段,但为什么人们在bash中使用条件句,比如
“x$variable”=“x”
,而不是更简单的
“$variable”=”
?有什么好处吗?或者这只是人们学习的方式吗?@Thor84no这是对旧的、有缺陷的系统的一种保护措施:几乎可以使用任何键:返回键和空格键显然没有被检测到(这里是OS X 10.10)。想法?你将如何恢复标准输入模式?哦,但我认为最好使用
stty--save
保存原始状态并在以后恢复。就像这样
同时!read-t0;做回显。;完成;阅读echo Finished
,但在按下Enter(或Ctrl-d)之前,它不会完成,即使使用可能的
-s
选项,它也会回显输入,并且不考虑可能的
-d
选项。(GNU bash,版本4.3.11)正如@jarno所提到的,这个答案是不正确的,因为
read-t0
只在按下Enter键时才将输入视为可用。@hackerb9谢谢,我添加了-n参数以避免需要Enter键。它可能需要非零超时才能工作,像这样:
echo-nx | read-t0.001-n1&&echo再次捕捉到它
,@jarno是正确的,尽管该解决方案的副作用是无限循环将在每次迭代中减慢1ms。Paul,我建议您将答案改为显示实际的bash而不是伪代码,这样您就可以验证它是否有效了。这不会中断一个循环,而是会中断整个过程script@mouviciel:没错,但是,如果在不退出整个脚本的情况下添加一些有关使用“trap foo SIGINT”捕获^C的信息,那就更好了。如果循环只等待按键,最好在while循环中添加例如
sleep 0.1
,这样循环就不会占用CPU核心的所有可用资源。