Ответ 1
read -rsn1
Ожидайте только одну букву (и не ждите отправки) и молчите (не пишите это письмо назад).
У меня есть оболочка script, которая по сути говорит что-то вроде
while true; do
read -r input
if ["$input" = "a"]; then
echo "hello world"
fi
done
Все хорошо, и хорошо, но я просто понял, что нужно нажать ENTER, это серьезная проблема в этой ситуации. Мне нужно, чтобы script отвечал, когда нажата клавиша, без нажатия клавиши ввода.
Есть ли способ достичь этой функциональности в оболочке script?
read -rsn1
Ожидайте только одну букву (и не ждите отправки) и молчите (не пишите это письмо назад).
поэтому окончательный рабочий фрагмент следующий:
#!/bin/bash
while true; do
read -rsn1 input
if [ "$input" = "a" ]; then
echo "hello world"
fi
done
Другой способ сделать это, не блокируя (не уверен, что это то, что вы хотите). Вы можете использовать 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, как будто script завершает работу до того, как вы вернете stty обратно в нормальное состояние, вы не сможете увидеть что-либо, что вы набираете, и он будет выглядеть замороженным.
trap control_c SIGINT
control_c()
{
stty sane
}
P.S Кроме того, вы можете захотеть поставить оператор сна в свой script, чтобы не использовать весь ваш процессор, так как он будет постоянно работать так быстро, как только может.
sleep 0.1
P.S.S Похоже, проблема зависания была только тогда, когда я использовал -echo, как я привык, поэтому, вероятно, не нужен. Я собираюсь оставить его в ответе, хотя по-прежнему полезно reset stty по умолчанию, чтобы избежать будущих проблем. Вы можете использовать -echo, если вы не хотите, чтобы вы отображались на экране.
Вы можете использовать эту функцию getkey
:
getkey() {
old_tty_settings=$(stty -g) # Save old settings.
stty -icanon
Keypress=$(head -c1)
stty "$old_tty_settings" # Restore old settings.
}
Он временно отключает "канонический режим" в настройках терминала
(stty -icanon
), а затем возвращает вход "head" (встроенная оболочка) с опцией -c1, которая возвращает ОДИН байт стандартного ввода. Если вы не включите "stty -icanon", то script повторяет букву нажатой клавиши, а затем ждет RETURN (не то, что мы хотим). Оба "head" и "stty" - это встроенные команды оболочки. Важно сохранить и восстановить старые параметры терминала после нажатия клавиши.
Затем 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
может использоваться для интерактивного взаимодействия многих сценариев оболочки. Надеюсь, это поможет.
У меня есть способ сделать это в моем проекте: 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