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
Ответы
Ответ 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
, потому что она является переменной по умолчанию.