Я работаю над секундомером для кубика Рубика, написанным на Bash, и мне бы хотелось останавливать его нажатием клавиши пробела, поскольку нажатие клавиши Ctrl«+» Cневыполнимо, когда нужно остановить его как можно быстрее.
Так есть ли способ остановить программу, просто нажав пробел?
Я посмотрел на команду, kill
которую не мог понять.
Часть кода, отвечающая за секундомер, приведена ниже:
TMP0=$(date +%s%N)
while true; do
TMP1=$(date +%s%N)
DTMP=$(($TMP1-$TMP0))
printf "\r$(($DTMP/1000000000)).${DTMP:(-9):2}"
done
решение1
Это ваш терминал отправляет SIGINT в группу процессов переднего плана, когда вы нажимаете Ctrl+ c. Вы можете настроить его так, чтобы он делал это для любого символа, но вы хотите восстановить старые настройки при выходе из скрипта.
#!/bin/bash
settings="$(stty -g)"
trap 'stty "$settings"' EXIT
stty intr ' '
TMP0=$(date +%s%N)
while true; do
TMP1=$(date +%s%N)
DTMP=$(($TMP1-$TMP0))
printf '\r%s' "$(($DTMP/1000000000)).${DTMP:(-9):2}"
done
settings="$(stty -g)"
сохраняет старые настройки; stty intr ' '
заставляет терминал реагировать на Spaceвместо Ctrl+ c; trap 'stty "$settings"' EXIT
отвечает за восстановление старых настроек при выходе из скрипта.
Примечания:
В коде ловушкиstty $settings
лучше, чемstty "$settings"
. Это исключение из "всегда цитируйте" правило. Кавычки предотвращают разбиение слов и генерацию имен файлов (подстановку), но некоторые реализацииstty
могут потребовать разбиения слов, для них переменная не должна заключаться в кавычки. Чтобы избежать вредных побочных эффектов от генерации имен файлов,Спецификациятребуетсяstty -g
сгенерировать строку, которая не активирует эту функцию.Редактировать:это был дефект в спецификации POSIX. Приведенный выше код был исправлен.
В случае, если что-то пойдет не так и вы окажетесь в интерактивной оболочке, где Spaceвсе еще генерируется SIGINT, вам нужно вызвать
stty intr ^C
вручную. В команде есть пробелы, но имейте в виду, что вы все равно можете ввести буквальный символ пробела, нажав Ctrl+ v Space.Я использовал его,
printf '\r%s' …
потому что считаю, что статический формат и динамические данные лучше соответствуют «философии»,printf
чем динамический формат, который использовали вы.
" Spaceвместо Ctrl+ c" означает, что когда Spaceстановится особенным, Ctrl+ cперестает быть особенным. Вы можете назвать это недостатком, потому чтопользователи будут ожидать , что Ctrl+ cостанется особенным. Однако сделать их одновременно особенными не так-то просто, я думаю, это невозможно сделать путем настройки терминала.
Эта альтернативная версия останавливается практически на любой клавише, в то время как Ctrl+ cработает как обычно:
#!/bin/bash
while read -r -t 0.001; do :; done # dump the buffer
TMP0=$(date +%s%N)
until read -r -n 1 -t 0.001; do
TMP1=$(date +%s%N)
DTMP=$(($TMP1-$TMP0))
printf '\r%s' "$(($DTMP/1000000000)).${DTMP:(-9):2}"
done
echo
Чтобы он реагировал Spaceтолько на то, что вам нужно проверить, что было прочитано:
…
until read -r -n 1 -t 0.001 && [ "$REPLY" = ' ' ]; do
…
Обратите внимание, что механизм Spaceостановки скрипта теперь сильно отличается от того, как работает Ctrl+ c.
решение2
Если вам не нужен секундомер, а вы просто хотите узнать, сколько времени в итоге потребовалось, вы можете использовать:
time read -r -n 1
это даст вам вывод вроде:
time read -r -n 1
real 1m30.909s
user 0m0.000s
sys 0m0.000s
и останавливаться практически при любом нажатии клавиши (подобно тому, как Камил использует read
)
или если вам нужна только часть времени:
{ time read -r -n 1; } 2>&1 | grep real | awk '{ print $2 }'
любезно предоставленоричи