Sobrescribir la salida anterior en Bash en lugar de agregarla


22

Para un temporizador bash, uso este código:

#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek "  
sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

Eso me da algo asi

One Moment please: 60 59 58 57 56 55 ...

¿Existe la posibilidad de reemplazar el último valor de segundo por el más reciente para que la salida no genere un gran rastro sino la cuenta regresiva de segundos como un tiempo real en una posición? (Espero que entiendas lo que quiero decir :))


Puede haber una manera de hacer esto con el watchcomando, aunque no estoy seguro de cómo hacerlo.
AJMansfield

Respuestas:


14
#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek" 

sleep 1
   sek=$[$sek-1]
   echo -en "\b\b"
done
echo
echo "ready!"

2
Increíble, muchas gracias. Solo un aviso para los otros lectores. Para ajustar el código anterior, debe usar echo -en "\ b \ b \ b" debido al espacio.
NES

1
+1 Agradable, pero ... por debajo de 10 comienza a comer el mensaje ...
lepe

1
Sí, mejor usar \r. Mira mi respuesta.
Mikel

3
Desde el manual de bash: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Utilice el POSIX $((expression))o el ((comando en su lugar. Por ejemplo, sek=$(( sek - 1 ))or (( sek = sek - 1 ))o (( sek-- )).
geirha 03 de

17

Básicamente es lo mismo que la respuesta de aneeshep, pero usa Return ( \r) en lugar de Retroceso ( \b) porque no sabemos si la longitud siempre será la misma, por ejemplo, cuándo $sek < 10.

Además, el primero echodebe usar $sek, no código duro 60.

Finalmente, observe el espacio después de ....

#!/bin/bash
sek=60
echo "$sek Seconds Wait!"
while [ $sek -ge 1 ]
do
   echo -ne "One Moment please $sek ... \r"
   sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

7

Con bash puedes usar la variable especial SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'

+1 Buena respuesta, además nunca supe de SEGUNDOS.
Mikel

Funciona, pero es un poco extraño ver 'dormir .5' como una condición probada de bucle while. acciones que requieren mucho tiempo, como con el sueño 1) ... SECONDS Esta variable se expande a la cantidad de segundos desde que se inició el shell. (así que probablemente no lo establecería en 0 ... solo
pruébalo

Estoy comentando más, solo porque estoy personalmente interesado en una forma de obtener una reducción precisa ... He probado $ SEGUNDOS y parece que si está configurado en '0' o no, todavía depende de segundo fraccional actual del sistema .. es decir. Establecerlo en '0' puede resultar en un tiempo de 0.99 ... (lo mismo es cierto si se deja como está) ... Entonces, su mejor probabilidad promedio es estar dentro de solo .5 de segundo. .. Solo un comentario (para eso están los comentarios :)
Peter.O

@ fred.bear Bueno, nunca lo entenderás de verdad; algún otro proceso podría comenzar a acaparar la CPU y / o la E / S mientras se realiza la cuenta regresiva y arruinar la precisión que tenía anteriormente. Lo que puede hacer es hacer que espere al menos X cantidad de tiempo, y dar una cuenta regresiva aproximada si lo desea. Cuando un programa dice "tomará un minuto", ¿espera que tome exactamente 1 minuto, al milisegundo? o tomar 1 minuto, dar o tomar unos segundos?
geirha

@geirha ... Sí, esa variación no importará en el 99% de los casos, y es genial que me hayas dado cuenta de $ SEGUNDOS ... Solo estoy encontrando sus límites ... He jugado con un variación de su secuencia de comandos que informa el tiempo de finalización basado en una suspensión de 0.01 segundos (además, como mencionó, cualquier retraso del sistema externo) pero solo se imprime cuando un segundo clic hace clic, pero luego descubrí que el primer 'segundo' se imprimía demasiado pronto (en un caso, justo después de los primeros .01 segundos) ... Todo es parte de mi curva de aprendizaje bash ... Me gusta tu respuesta, es por eso que la he examinado más a fondo.
Peter.O

3

Además de \ro se \bacerca, es posible usar el \033[2K carácter de control , que le dice al terminal que limpie toda la línea. La ventaja de esto en comparación con \bes que no tiene que hacer coincidir el número \bcon la cantidad de caracteres que desea eliminar, y en comparación \rno habrá caracteres sobresaliendo en la pantalla si la nueva línea es más corta que la anterior uno.

A continuación se muestra el ejemplo de cómo se puede aplicar a esta pregunta, y aquí hay un ejemplo de la aplicación relacionada para crear resultados similares a los mensajes de arranque. En este ejemplo en particular, el temporizador desaparecerá una vez que se alcance el segundo segundo y la línea del temporizador se reemplazará con "¡Listo!" frase.

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    printf "One moment please: %d" "$sek"
    sleep 1
    printf "\r%b" "\033[2K"
done
echo "Ready!"

Otra alternativa sería emplear el dialogcomando para crear cuadros de diálogo simples en la línea de comandos. El cuadro de diálogo permanecerá en la pantalla mientras dure el temporizador y se actualizará con el bucle, y para cuando termine, el temporizador se reemplazará con el mensaje "¡Listo! Presione para salir" de manera transparente:

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    echo "$sek" | dialog --progressbox "Please wait" 10 25
    sleep 1
done
dialog --msgbox "Ready! Press <OK> to finish" 10 25

dialogno existe en mac: /
Ricky Levi

1

Esto es lo que se me ocurrió después de leer aquí y un poco más, el único revestimiento:

SEC=101;for i in `seq $SEC -1 1`;do printf "\rNext in: %`expr length $SEC`ds" "$i";sleep 1;done;echo

Más legible:

#!/bin/bash
SEC=101

for i in `seq $SEC -1 1`;do
        printf "\rNext in: %`expr length $SEC`ds" "$i";
        sleep 1;
done
echo

Donde SEC se puede establecer en cualquier número entero positivo y printf se encargará del relleno apropiado. Probado bajo Ubuntu y cygwin.


1

Puede lograrlo colocando el retorno de carro \r.

En una sola línea de código, es posible con echo -ne

for i in {60..1}; do echo -ne "One moment please: $i\r" && sleep 1; done

o con printf

for i in {60..1}; do printf "One moment please: $i\r" && sleep 1; done

-2

Contador regresivo:

MIN=1 && for i in $(seq $(($MIN*60)) -1 1); do echo -n "$i, "; sleep 1; done; echo -e "nnMessage"

y el cronómetro 'normal' es:

START=$( date +%s ); while true; do CURRENT=$( date +%s ) ; echo $(( CURRENT-START )) ; sleep 1 ; echo -n  ; done

control + c para detener


Esto no responde la pregunta sobre la sobrescritura de texto
Jeremy Kerr
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.