Estoy trabajando en un cronómetro del Cubo de Rubik escrito en Bash y me gustaría detenerlo presionando la barra espaciadora ya que Ctrl+ Cno es factible cuando tienes que terminarlo lo más rápido que puedas.
Entonces, ¿hay alguna manera de detener el programa simplemente con la barra espaciadora?
Miré el comando kill
pero no pude entenderlo.
La parte del cronómetro del código se encuentra a continuación:
TMP0=$(date +%s%N)
while true; do
TMP1=$(date +%s%N)
DTMP=$(($TMP1-$TMP0))
printf "\r$(($DTMP/1000000000)).${DTMP:(-9):2}"
done
Respuesta1
Es su terminal quien envía SIGINT al grupo de procesos de primer plano cuando presiona Ctrl+ c. Puede configurarlo para que haga esto con cualquier personaje, pero desea restaurar la configuración anterior cuando finalice el script.
#!/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)"
guarda la configuración anterior; stty intr ' '
hace que el terminal reaccione en Spacelugar de Ctrl+ c; trap 'stty "$settings"' EXIT
es responsable de restaurar la configuración anterior cuando se cierra el script.
Notas:
En el código de la trampa,stty $settings
es mejor questty "$settings"
. Es una excepción del "cita siempre" regla. Las comillas evitan la división de palabras y la generación de nombres de archivos (globbing), pero algunas implementaciones destty
pueden requerir la división de palabras, para ellas la variable no debe estar entre comillas. Para evitar efectos secundarios dañinos de la generación de nombres de archivos, laespecificaciónrequierestty -g
generar una cadena que no active esta característica.Editar:eso fue un defecto en la especificación POSIX. El código anterior ha sido corregido.
En caso de que algo salga mal y te encuentres en un shell interactivo donde Spaceaún se genera SIGINT, debes invocarlo
stty intr ^C
manualmente. Hay espacios en el comando, pero tenga en cuenta que aún puede escribir un carácter de espacio literal presionando Ctrl+ v Space.Lo usé
printf '\r%s' …
porque creo que un formato estático y datos dinámicos se ajustanprintf
mejor a la "filosofía" que el formato dinámico que usaste.
" Spaceen lugar de Ctrl+ c" significa que cuando Spacese vuelve especial, Ctrl+ cdeja de ser especial. Puedes llamarlo un defecto porquelos usuarios esperarán que Ctrl+ csiga siendo especial. Sin embargo, hacer que ambos sean especiales al mismo tiempo no es trivial, creo que no se puede hacer configurando el terminal.
Esta versión alternativa se detiene en casi cualquier tecla, mientras que Ctrl+ cfunciona como siempre:
#!/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
Para que reaccione Spacesólo es necesario comprobar lo leído:
…
until read -r -n 1 -t 0.001 && [ "$REPLY" = ' ' ]; do
…
Tenga en cuenta que el mecanismo para Spacedetener el script ahora es muy diferente al funcionamiento de Ctrl+ c.
Respuesta2
Si no necesitas el cronómetro, después de todo, solo quieres saber el tiempo que tomó al final, puedes usar:
time read -r -n 1
te dará una salida como:
time read -r -n 1
real 1m30.909s
user 0m0.000s
sys 0m0.000s
y detenerse con prácticamente cualquier pulsación de tecla (similar a cómo usa Kamil read
)
o si solo quieres la parte del tiempo:
{ time read -r -n 1; } 2>&1 | grep real | awk '{ print $2 }'
cortesía derico