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

时间:2011-03-14 10:56:40

标签: 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."
exit 0

6 个答案:

答案 0 :(得分:33)

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

#!/bin/bash

if [ -t 0 ]; then 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 sane; fi

echo "You pressed '$keypress' after $count loop iterations"
echo "Thanks for using this script."
exit 0

修改2014/12/09:-icrnl标记添加到stty以正确捕获Return键,使用cat -v代替read为了抓住空间。

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

答案 1 :(得分:8)

read具有可以使用的超时参数-t。对输入执行非阻塞检查,查看返回状态是否为0,如果是,则从循环中断。

来自bash manual

  

-t 超时

     

如果在 timeout 秒内未读取完整的输入行,则读取会超时并返回失败。 timeout 可以是十进制数,小数点后面有小数部分。此选项仅在读取从终端,管道或其他特殊文件读取输入时有效;从常规文件中读取时没有任何效果。如果 timeout 为0,如果输入在指定的文件描述符上可用,读取将返回成功,否则失败。   如果超出超时,则退出状态大于128.

Here一些使用示例。

答案 2 :(得分:2)

通常我不介意用简单的CTRL-C打破bash无限循环。这是终止tail -f的传统方式。

答案 3 :(得分:0)

这是另一种解决方案。它适用于任何按下的按键,包括空格,输入,箭头等。

在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

答案 4 :(得分:0)

我发现了this forum post,并将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"

运行this script

答案 5 :(得分:0)

:循环中无人值守的用户输入

我已经做到这一点,而不必和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