Подтвердить что ты не робот

Bash: Как закончить бесконечный цикл нажатием любой клавиши?

Мне нужно написать бесконечный цикл, который останавливается при нажатии любой клавиши.

К сожалению, это происходит только при нажатии клавиши.

Идеи пожалуйста?

#!/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
4b9b3361

Ответ 1

Вам нужно поставить стандартный ввод в неблокирующем режиме. Вот пример, который работает:

#!/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, чтобы правильно перехватить клавишу Return, используйте cat -v вместо read, чтобы поймать пробел.

Возможно, что cat читает более одного символа, если он достаточно быстро передает данные; если это не так, замените cat -v на dd bs=1 count=1 status=none | cat -v.

Изменить 2019/09/05: Используйте stty --save для восстановления настроек TTY.

Ответ 2

read имеет параметр количества символов -n и параметр времени ожидания -t, который можно использовать.

Из руководства по bash:

-n nchars чтение возвращается после чтения символов nchars, а не ожидания полной строки ввода, но учитывает разделитель, если перед разделителем читается меньше символов nchars.

-t timeout

Вызывает чтение тайм-аута и возвращает ошибку, если полная строка ввода (или указанное количество символов) не читается в течение тайм-аута. Тайм-аут может быть десятичным числом с дробной частью после десятичной точки. Эта опция эффективна, только если read читает входные данные из терминала, канала или другого специального файла; это не влияет на чтение из обычных файлов. Если время чтения истекло, чтение сохраняет любой частичный ввод, считанный в указанное имя переменной. Если тайм-аут равен 0, чтение сразу возвращается, не пытаясь прочитать какие-либо данные. Статус выхода равен 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

Ответ 3

Обычно я не против разбивать бесконечный цикл bash с помощью простого CTRL-C. Это традиционный способ для завершения tail -f.

Ответ 4

Вот еще одно решение. Он работает для любой нажатой клавиши, включая пробел, ввод, стрелки и т.д.

Исходное решение, протестированное в 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 вы даже можете опустить переменную REPLY для команды read, потому что она является переменной по умолчанию.