¿Hay alguna manera de mostrar una cuenta regresiva o un cronómetro en una terminal?

144

¿Cómo puedo mostrar un temporizador de cuenta regresiva en tiempo real en el terminal de Linux? ¿Existe una aplicación existente o, mejor aún, una línea para hacer esto?

tir38
fuente

Respuestas:

184

No estoy seguro de por qué lo necesita beep, si todo lo que quiere es un cronómetro, puede hacer esto:

while true; do echo -ne "`date`\r"; done

Eso le mostrará los segundos que pasan en tiempo real y puede detenerlo con Ctrl+ C. Si necesita mayor precisión, puede usar esto para darle nanosegundos:

while true; do echo -ne "`date +%H:%M:%S:%N`\r"; done

Finalmente, si realmente quieres el "formato de cronómetro", donde todo comienza en 0 y comienza a crecer, puedes hacer algo como esto:

date1=`date +%s`; while true; do 
   echo -ne "$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r";
done

Para un temporizador de cuenta regresiva (que no es lo que pidió su pregunta original), puede hacer esto (cambiar los segundos en consecuencia):

seconds=20; date1=$((`date +%s` + $seconds)); 
while [ "$date1" -ge `date +%s` ]; do 
  echo -ne "$(date -u --date @$(($date1 - `date +%s` )) +%H:%M:%S)\r"; 
done

Puede combinarlos en comandos simples usando las funciones bash (o el shell que prefiera). En bash, agregue estas líneas a su ~/.bashrc(esto sleep 0.1hará que el sistema espere 1/10 de segundo entre cada ejecución para que no envíe spam a su CPU):

function countdown(){
   date1=$((`date +%s` + $1)); 
   while [ "$date1" -ge `date +%s` ]; do 
     echo -ne "$(date -u --date @$(($date1 - `date +%s`)) +%H:%M:%S)\r";
     sleep 0.1
   done
}
function stopwatch(){
  date1=`date +%s`; 
   while true; do 
    echo -ne "$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r"; 
    sleep 0.1
   done
}

Luego puede iniciar un temporizador de cuenta regresiva de un minuto ejecutando:

countdown 60

Puede realizar una cuenta regresiva de dos horas con:

countdown $((2*60*60))

o un día entero usando:

countdown $((24*60*60))

Y comience el cronómetro ejecutando:

stopwatch

Si necesita poder lidiar con días, horas, minutos y segundos, puede hacer algo como esto:

countdown(){
    date1=$((`date +%s` + $1));
    while [ "$date1" -ge `date +%s` ]; do 
    ## Is this more than 24h away?
    days=$(($(($(( $date1 - $(date +%s))) * 1 ))/86400))
    echo -ne "$days day(s) and $(date -u --date @$(($date1 - `date +%s`)) +%H:%M:%S)\r"; 
    sleep 0.1
    done
}
stopwatch(){
    date1=`date +%s`; 
    while true; do 
    days=$(( $(($(date +%s) - date1)) / 86400 ))
    echo -ne "$days day(s) and $(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r";
    sleep 0.1
    done
}

Tenga en cuenta que la stopwatchfunción no se ha probado durante días, ya que realmente no quería esperar 24 horas. Debería funcionar, pero avíseme si no es así.

terdon
fuente
99
Agregué estas funciones agradables a mi .zshrcderecha después de leer su respuesta. Hoy usé countdownla primera vez y noté un uso bastante alto de la CPU. Agregué un sleep 0.1(no sé si un tiempo de reposo de un segundo fraccional es compatible con todos los sistemas) que mejoró mucho eso. El inconveniente es, por supuesto, una precisión de visualización menos precisa, pero puedo vivir con una desviación máxima. 100ms.
mpy
@mpy gracias por mencionar eso. Estoy obteniendo ~ 3% de uso de CPU al hacer esto bash, puedo vivir con eso (aunque es más alto de lo que esperaba y también he agregado el sueño a mi propio .bashrc).
terdon
3
Acabo de encontrar esta respuesta. Descubrí que poner el retorno de carro al comienzo de la declaración de eco en la función de cronómetro era útil, ya que significaba que matar el cronómetro no sobrescribía el tiempo actual del cronómetro: echo -ne "\ r $ (date -u --date @ $ (( date +%s- $ date1)) +% H:% M:% S) ";
mkingston
3
@chishaku: En OS X, esto parecía funcionar para mí: echo -ne "$(date -ju -f %s $(($date1 - fecha +% s )) +%H:%M:%S)\r";
user1071847
1
Con el paquete sox me gustaría añadir un sonido agradable para jugar al final de la cuenta atrás: play -q -n synth 2 pluck C5.
Pablo A
94

Mi forma favorita es:

Comienzo:

time cat

Detener:

ctrl+c

Como @wjandrea comentó a continuación, se ejecutará otra versión:

time read

y presione Enterpara detener

alfasin
fuente
8
similar, pero puede presionar Enter para detenerlo:time read
wjandrea
10
time readfalla en zsh, pero time (read)funciona.
Sparhawk
Sin embargo, el problema es que no puede ver la hora sin cancelarla o terminarla. Cuando lo reinicia, el temporizador comienza de nuevo.
Zelphir Kaltstahl
39

Estaba buscando lo mismo y terminé escribiendo algo más elaborado en Python:

Esto te dará una simple cuenta regresiva de 10 segundos:

sudo pip install termdown
termdown 10

Fuente: https://github.com/trehn/termdown

trehn
fuente
1
@DoktoroReichard: Bueno, este es un enlace de descarga.
harrymc
1
@Suhaib: Debería y lo hace por mí. Plantee un problema en GitHub con más información.
trehn
1
Me encanta, este es mi callejón
Wayne Werner
1
Muy bien, me gusta el ASCII
Guillermo
1
¡Esto es realmente genial! 😄
orschiro
13

He usado este:

countdown()
(
  IFS=:
  set -- $*
  secs=$(( ${1#0} * 3600 + ${2#0} * 60 + ${3#0} ))
  while [ $secs -gt 0 ]
  do
    sleep 1 &
    printf "\r%02d:%02d:%02d" $((secs/3600)) $(( (secs/60)%60)) $((secs%60))
    secs=$(( $secs - 1 ))
    wait
  done
  echo
)

Ejemplo:

 countdown "00:07:55"

Aquí hay una fuente .

Adobe
fuente
2
Compatible con POSIX - funciona en OS X :)
djule5
13
sh-3.2# man leave

establecer un temporizador para 15 minutos

sh-3.2# leave +0015
Alarm set for Thu Nov  3 14:19:31 CDT 2016. (pid 94317)
sh-3.2#

editar: tenía un montón de enlaces abiertos, y pensé que esto era específico de osx, lo siento. Dejando mi respuesta para que otros estén al tanto de la licencia en los BSD.

efk
fuente
1
leave también funciona en Linux, pero primero debes instalar el programa de licencia desde los repositorios predeterminados.
karel
6

Esto es para un cronómetro con centésimas de segundo:

#!/usr/bin/awk -f
function z() {
  getline < "/proc/uptime"
  close("/proc/uptime")
  return $0
}
BEGIN {
  x = z()
  while (1) {
    y = z()
    printf "%02d:%05.2f\r", (y - x) / 60, (y - x) % 60
  }
}

Ejemplo

Steven Penny
fuente
¡Buena solución solo para Linux! Me encanta la falta de llamadas externas. Tenga en cuenta que mi sistema Debian tiene dos valores en / proc / uptime, y el segundo probablemente se refiere a mi tiempo de actividad anterior. Este script se puede ajustar para remediar eso cambiando la línea 5 areturn $1
Adam Katz
4

He combinado la muy buena respuesta del terdon, en una función que al mismo tiempo muestra la hora desde el inicio y el tiempo hasta el final. También hay tres variantes, por lo que es más fácil llamar (no tienes que hacer matemáticas bash), y también es abstracto. Ejemplo de uso :

{ ~ }  » time_minutes 15
Counting to 15 minutes
Start at 11:55:34     Will finish at 12:10:34
     Since start: 00:00:08     Till end:  00:14:51

Y algo así como el temporizador de trabajo:

{ ~ }  » time_hours 8
Counting to 8 hours
Start at 11:59:35   Will finish at 19:59:35
     Since start: 00:32:41     Till end:  07:27:19

Y si necesitas un tiempo muy específico:

{ ~ }  » time_flexible 3:23:00
Counting to 3:23:00 hours
Start at 12:35:11   Will finish at 15:58:11
     Since start: 00:00:14     Till end:  03:22:46

Aquí está el código para poner en su .bashrc

function time_func()
{
   date2=$((`date +%s` + $1));
   date1=`date +%s`;
   date_finish="$(date --date @$(($date2)) +%T )"

   echo "Start at `date +%T`   Will finish at $date_finish"

    while [ "$date2" -ne `date +%s` ]; do
     echo -ne "     Since start: $(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)     Till end:  $(date -u --date @$(($date2 - `date +%s`)) +%H:%M:%S)\r";
     sleep 1
    done

    printf "\nTimer finished!\n"
    play_sound ~/finished.wav
}

function time_seconds()
{
  echo "Counting to $1 seconds"
  time_func $1
}

function time_minutes()
{
  echo "Counting to $1 minutes"
  time_func $1*60
}

function time_hours()
{
  echo "Counting to $1 hours"
  time_func $1*60*60
}

function time_flexible()  # accepts flexible input hh:mm:ss
{
    echo "Counting to $1"
    secs=$(time2seconds $1)
    time_func $secs
}

function play_sound()  # adjust to your system
{
    cat $1 > /dev/dsp
}

function time2seconds() # changes hh:mm:ss to seconds, found on some other stack answer
{ 
    a=( ${1//:/ }) 
    echo $((${a[0]}*3600+${a[1]}*60+${a[2]})) 
}

Combina esto con alguna forma de reproducir sonido en la terminal de Linux ( reproduce archivos mp3 o wav a través de la línea de comando de Linux ) o cygwin ( cat /path/foo.wav > /dev/dsp funciona para mí en babun / win7) y tienes un temporizador flexible simple con alarma .

Koshmaar
fuente
4

Otro enfoque

countdown=60 now=$(date +%s) watch -tpn1 echo '$((now-$(date +%s)+countdown))'

Para Mac:

countdown=60 now=$(date +%s) watch -tn1 echo '$((now-$(date +%s)+countdown))'
#no p option on mac for watch

Si se quiere una señal cuando llega a cero, se podría construir, por ejemplo, con un comando que devolviera un estado de salida distinto de cero en cero y combinarlo con watch -balgo, o algo así, pero si se quiere construir un script más elaborado, esto probablemente sea no es el camino a seguir; es más una solución de tipo "rápido y sucio".


Me gusta el watchprograma en general. Lo vi por primera vez después de haber escrito innumerables while sleep 5; dobucles con diferentes efectos. watchfue demostrablemente más agradable.

Daniel Andersson
fuente
3

Terminé escribiendo mi propio script de shell: github gist

#!/bin/sh
# script to create timer in terminal
# Jason Atwood
# 2013/6/22

# start up
echo "starting timer script ..."
sleep 1 # seconds

# get input from user
read -p "Timer for how many minutes?" -e DURATION
DURATION=$(( $DURATION*60 )) # convert minutes to seconds

# get start time
START=$(date +%s)

# infinite loop
while [ -1 ]; do
clear # clear window

# do math
NOW=$(date +%s) # get time now in seconds
DIF=$(( $NOW-$START ))  # compute diff in seconds
ELAPSE=$(( $DURATION-$DIF ))    # compute elapsed time in seconds
MINS=$(( $ELAPSE/60 ))  # convert to minutes... (dumps remainder from division)
SECS=$(( $ELAPSE - ($MINS*60) )) # ... and seconds

# conditional
if [ $MINS == 0 ] && [ $SECS == 0 ] # if mins = 0 and secs = 0 (i.e. if time expired)
then # blink screen
for i in `seq 1 180`; # for i = 1:180 (i.e. 180 seconds)
do
clear # flash on
setterm -term linux -back red -fore white # use setterm to change background color
echo "00:00 " # extra tabs for visibiltiy

sleep 0.5

clear # flash off
setterm -term linux -default # clear setterm changes from above
echo "00:00" # (i.e. go back to white text on black background)
sleep 0.5
done # end for loop
break   # end script

else # else, time is not expired
echo "$MINS:$SECS"  # display time
sleep 1 # sleep 1 second
fi  # end if
done    # end while loop 
tir38
fuente
1
Buen guión, +1. Para que lo sepas, es un temporizador de cuenta regresiva, no un cronómetro.
terdon
ja tienes razón, eso es lo que realmente quería. Actualizaré mi nombre.
tir38
3

Me sorprende que nadie haya usado la sleepenhherramienta en sus scripts. En cambio, las soluciones propuestas usan un sleep 1entre salidas de temporizador posteriores o un bucle ocupado que sale lo más rápido posible. La primera es inadecuada porque debido al poco tiempo que se dedica a imprimir, la salida no se realizará realmente una vez por segundo, sino un poco menos de lo que no es óptimo. Después de que pase suficiente tiempo, el contador saltará un segundo. Este último es inadecuado porque mantiene ocupada la CPU sin una buena razón.

La herramienta que tengo en mi se $PATHve así:

#!/bin/sh
if [ $# -eq 0 ]; then
    TIMESTAMP=$(sleepenh 0)
    before=$(date +%s)
    while true; do
        diff=$(($(date +%s) - before))
        printf "%02d:%02d:%02d\r" $((diff/3600)) $(((diff%3600)/60)) $((diff%60))
        TIMESTAMP=$(sleepenh $TIMESTAMP 1.0);
    done
    exit 1 # this should never be reached
fi
echo "counting up to $@"
"$0" &
counterpid=$!
trap "exit" INT TERM
trap "kill 0" EXIT
sleep "$@"
kill $counterpid

El script puede usarse como un cronómetro (contando hasta que se interrumpe) o como un temporizador que se ejecuta durante el tiempo especificado. Como sleepse usa el comando, este script permite especificar la duración para la cual contar con la misma precisión que sleeppermite. En Debian y derivados, esto incluye durmientes de menos de un segundo y una buena forma legible por humanos para especificar el tiempo. Entonces, por ejemplo, puedes decir:

$ time countdown 2m 4.6s
countdown 2m 4.6s  0.00s user 0.00s system 0% cpu 2:04.60 total

Y como puede ver, el comando se ejecutó exactamente durante 2 minutos y 4.6 segundos sin mucha magia en el guión mismo.

EDITAR :

La herramienta Sleepenh proviene del paquete del mismo nombre en Debian y sus derivados como Ubuntu. Para las distribuciones que no lo tienen, proviene de https://github.com/nsc-deb/sleepenh

La ventaja de Sleepenh es que es capaz de tener en cuenta el pequeño retraso que se acumula con el tiempo por el procesamiento de otras cosas además del sueño durante un ciclo. Incluso si uno solo realizara sleep 1un bucle 10 veces, la ejecución general tomaría un poco más de 10 segundos debido a la pequeña sobrecarga que proviene de ejecutar sleepe iterar el bucle. Este error se acumula lentamente y con el tiempo hará que nuestro cronómetro sea cada vez más impreciso. Para solucionar este problema, cada iteración del ciclo debe calcular el tiempo preciso para dormir, que generalmente es un poco menos de un segundo (para temporizadores de intervalo de un segundo). La herramienta Sleepenh hace esto por usted.

josch
fuente
No entiendo qué beneficio da esto sobre mi respuesta que también usa sleep 0.1. ¿Qué es sleepnh(no puedo encontrarlo en los repositorios de Arch) y en qué se diferencia sleep? Por lo que puedo decir, básicamente estás haciendo lo mismo que mi respuesta anterior. ¿Qué me estoy perdiendo?
terdon
@terdon Sleepenh viene de aquí github.com/nsc-deb/sleepenh El problema con solo decir sleep 5es que no duermes exactamente 5 segundos. Pruebe por ejemplo time sleep 5y verá que ejecutar el comando tarda un poco más de 5 segundos. Con el tiempo se acumulan los errores. La utilidad sleepenh permite evitar fácilmente esta acumulación de errores.
josch
OKAY. En mi sistema veo un error de 0.002 segundos. Realmente dudo que alguien use este tipo de herramienta y espere una precisión superior a milisegundos, pero sería mejor si al menos edita su respuesta y yo) explique por qué sleepnhes mejor que sleep(solo dice que otras respuestas usan sleep 1, que no lo hacen) , solo el OP usa eso) y ii) dónde obtenerlo y cómo instalarlo, ya que no es una herramienta estándar.
terdon
1
@terdon Le expliqué la diferencia entre sleepy sleepenhen el primer párrafo. De todos modos, probablemente no estaba lo suficientemente claro sobre eso, así que me expandí más al final. Los problemas de precisión de milisegundos son los que se obtienen al llamar sleepuna vez. Se acumulan con el tiempo y en algún momento es notable. No dije que otros solo usan sleep 1. Dije que usan sleep 1o un busyloop. Lo que aún hacen. Muéstrame un contraejemplo. Las respuestas que hacen sleep 0.1son las mismas que las que hacen, sleep 1excepto que acumulan errores aún más rápido.
josch
Bajé las respuestas buscando a alguien que al menos reconociera la existencia del problema que resolviste.
mariotomo
2

Respuesta corta:

for i in `seq 60 -1 1` ; do echo -ne "\r$i " ; sleep 1 ; done

Explicación:

Sé que hay muchas respuestas, pero solo quiero publicar algo muy cercano a la pregunta de OP, que personalmente aceptaría como " cuenta regresiva en línea en la terminal ". Mis metas fueron:

  1. Un trazador de líneas.
  2. Cuenta regresiva.
  3. Fácil de recordar y escribir en la consola (sin funciones y lógica pesada, solo bash).
  4. No requiere la instalación de software adicional (se puede usar en cualquier servidor al que acceda mediante ssh, incluso si no tengo root allí).

Cómo funciona:

  1. seq imprime números del 60 al 1.
  2. echo -ne "\r$i "devuelve el cursor al comienzo de la cadena e imprime el $ivalor actual . Espacio después de que se requiere para sobrescribir el valor anterior, si fue más largo en caracteres que el actual $i(10 -> 9).
amonestar
fuente
1
Esto funcionó mejor para mi caso de uso en Mac OS para usar en un script bash. La explicación de cómo funciona es muy útil.
James Campbell
1

Para referencia futura, existe una herramienta de línea de comando llamada µTimer con opciones de línea de comando muy sencillas para un temporizador de cuenta regresiva / cuenta regresiva.

Tom
fuente
1

Imagina que eres una persona en OSX que busca un cronómetro de línea de comandos. Imagina que no quieres instalar las herramientas de GNU y solo quieres ejecutar con el Unixdate

en ese caso, haga lo que dice @terdon pero con esta modificación:

function stopwatch(){
    date1=`date +%s`; 
    while true; do 
        echo -ne "$(date -jf "%s" $((`date +%s` - $date1)) +%H:%M:%S)\r"; 
        sleep 0.1
    done
}
joem
fuente
1
Lo probé en OS X El Capitan, por alguna razón comienza con las 16:00:00
nopole
1

Simplemente use watch + date en hora UTC. También puede instalar algún paquete para pantalla grande ...

export now="`date +%s -u`";
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now ))'

#Big plain characters
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now )) | toilet -f mono12'

#Big empty charaters
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now )) | figlet -c -f big'

¡Intentalo!

Ver también http://www.cyberciti.biz/faq/create-large-colorful-text-banner-on-screen/

MUY Bélgica
fuente
1

sw es un cronómetro simple que funcionará para siempre.

SO

Instalar en pc

wget -q -O - http://git.io/sinister | sh -s -- -u https://raw.githubusercontent.com/coryfklein/sw/master/sw

Uso

sw
 - start a stopwatch from 0, save start time in ~/.sw
sw [-r|--resume]
 - start a stopwatch from the last saved start time (or current time if no last saved start time exists)
 - "-r" stands for --resume
Cory Klein
fuente
1

Un ejemplo de python:

#!/usr/bin/python

def stopwatch ( atom = .01 ):
    import time, sys, math

    start = time.time()
    last = start
    sleep = atom/2
    fmt = "\r%%.%sfs" % (int(abs(round(math.log(atom,10))))  if atom<1 else "")
    while True:
        curr = time.time()
        subatom = (curr-last)
        if subatom>atom:
            # sys.stdout.write( "\r%.2fs" % (curr-start))
            sys.stdout.write( fmt % (curr-start))
            sys.stdout.flush()
            last = curr
        else:
            time.sleep(atom-subatom)

stopwatch()

manifestación

user84207
fuente
1

Eche un vistazo a TermTime , es un buen reloj y cronómetro basado en terminal:

pip install termtime

vimist
fuente
0

Esto es similar a la respuesta aceptada, pero terdon countdown()me dio errores de sintaxis. Sin embargo, este funciona muy bien para mí:

function timer() { case "$1" in -s) shift;; *) set $(($1 * 60));; esac; local S=" "; for i in $(seq "$1" -1 1); do echo -ne "$S\r $i\r"; sleep 1; done; echo -e "$S\rTime's up!"; }

Puede ponerlo .bashrcy luego ejecutarlo con: timer t(donde t es tiempo en minutos).

Andy Forceno
fuente
0

Encontré esta pregunta hoy, cuando buscaba una aplicación de término para mostrar un temporizador de cuenta regresiva grande para un taller. Ninguna de las sugerencias era exactamente lo que necesitaba, por lo que rápidamente puse otra en Go: https://github.com/bnaucler/cdown

Como la pregunta ya está suficientemente respondida, considere que esto es por el bien de la posteridad.

B Nauclér
fuente
0

Una versión GUI del cronómetro

date1=`date +%s`
date1_f=`date +%H:%M:%S____%d/%m`
(
  while true; do 
    date2=$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)
    echo "# started at $date1_f \n$date2"
  done
) |
zenity --progress \
  --title="Stop Watch" \
  --text="Stop Watch..." \
  --percentage=0
Mohamed Samy
fuente
-2

Si desea un programa compilable por cualquier razón, lo siguiente funcionaría:

#include <iostream>
#include <string>
#include <chrono>

int timer(seconds count) {
  auto t1 = high_resolution_clock::now();
  auto t2 = t1+count;
  while ( t2 > high_resolution_clock::now()) {
    std::cout << "Seconds Left:" <<
    std::endl <<
      duration_cast<duration<double>>(count-(high_resolution_clock::now()-t1)).count() << 
    std::endl << "\033[2A\033[K";
    std::this_thread::sleep_for(milliseconds(100));
  }
  std::cout << "Finished" << std::endl;
  return 0;
}

Esto se puede usar en otros programas también y se puede portar fácilmente, si un entorno bash no está disponible o simplemente prefiere usar un programa compilado

github

elder4222
fuente