C'è un modo per visualizzare un conto alla rovescia o un cronometro in un terminale?


144

Come posso visualizzare un conto alla rovescia in tempo reale sul terminale Linux? Esiste un'app esistente o, meglio ancora, una linea per farlo?

Risposte:


184

Non sono sicuro del motivo per cui hai bisogno beep, se tutto ciò che vuoi è un cronometro, puoi farlo:

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

Questo ti mostrerà i secondi che passano in tempo reale e puoi fermarlo con Ctrl+ C. Se hai bisogno di maggiore precisione, puoi usarlo per darti nanosecondi:

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

Infine, se vuoi davvero "formato cronometro", dove tutto inizia da 0 e inizia a crescere, potresti fare qualcosa del genere:

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

Per un conto alla rovescia (che non è quello che la tua domanda originale ti ha chiesto) potresti farlo (cambia i secondi di conseguenza):

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

Puoi combinarli in semplici comandi usando le funzioni bash (o qualunque shell tu preferisca). In bash, aggiungi queste righe al tuo ~/.bashrc(il sleep 0.1sistema farà attendere 1/10 di secondo tra ogni esecuzione in modo da non spammare la tua 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
}

È quindi possibile avviare un conto alla rovescia di un minuto eseguendo:

countdown 60

Puoi contare per due ore con:

countdown $((2*60*60))

o un'intera giornata usando:

countdown $((24*60*60))

E avvia il cronometro eseguendo:

stopwatch

Se devi essere in grado di gestire giorni, ore, minuti e secondi, potresti fare qualcosa del genere:

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
}

Si noti che la stopwatchfunzione non è stata testata per giorni poiché non volevo davvero aspettare 24 ore per questo. Dovrebbe funzionare, ma per favore fatemi sapere se non funziona.


9
Ho aggiunto queste simpatiche funzioni alla mia .zshrcdestra dopo aver letto la tua risposta. Oggi ho usato countdownla prima volta e ho notato un utilizzo della CPU piuttosto elevato. Ho aggiunto un sleep 0.1(non so se un tempo di sospensione di un secondo frazionario è supportato su tutti i sistemi) che ha migliorato molto. Lo svantaggio è ovviamente una precisione di visualizzazione meno accurata, ma posso vivere con una deviazione di max. 100ms.
mpy,

@mpy grazie per averlo menzionato. Ricevo un ~ 3% di utilizzo della CPU quando lo faccio bash, posso conviverci (anche se è più alto di quanto mi aspettassi e ho anche aggiunto il sonno al mio .bashrc).
terdon,

3
Ho appena trovato questa risposta. Ho trovato utile inserire il ritorno a capo all'inizio dell'istruzione echo nella funzione cronometro poiché significava che uccidere il cronometro non sovrascriveva il tempo corrente del cronometro: echo -ne "\ r $ (date -u --date @ $ (( date +%s- $ date1)) +% H:% M:% S) ";
mkingston

3
@chishaku: Su OS X, questo sembra funzionare per me: echo -ne "$(date -ju -f %s $(($date1 - data +% s )) +%H:%M:%S)\r";
user1071847

1
Con il pacchetto sox vorrei aggiungere un suono bello giocare alla fine del conto alla rovescia: play -q -n synth 2 pluck C5.
Pablo A

94

Il mio modo preferito è:

Inizio:

time cat

Fermare:

ctrl+c

Come @wjandrea ha commentato di seguito, è necessario eseguire un'altra versione:

time read

e premere Enterper interrompere



8
simile, ma puoi premere Invio per fermarlo:time read
wjandrea,

10
time readfallisce in zsh, ma time (read)funziona.
Sparhawk,

Il problema è però che non è possibile vedere l'ora senza annullarla o terminarla. Quando lo riavvii, il timer inizia di nuovo.
Zelphir Kaltstahl,

39

Stavo cercando la stessa cosa e ho finito per scrivere qualcosa di più elaborato in Python:

Questo ti darà un semplice conto alla rovescia di 10 secondi:

sudo pip install termdown
termdown 10

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


1
@DoktoroReichard: bene, questo è un link per il download.
harrymc,

1
@Suhaib: dovrebbe e fa per me. Si prega di sollevare un problema su GitHub con maggiori informazioni.
Trehn,

1
Lo adoro - questo è proprio il mio vicolo
Wayne Werner,

1
Molto bello, mi piace l'ASCII
Guillermo il

1
Questo è davvero fantastico! 😄
orschiro

13

Ho usato questo:

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
)

Esempio:

 countdown "00:07:55"

Ecco una fonte .


2
Conforme POSIX - funziona su OS X :)
djule5

13
sh-3.2# man leave

impostare un timer per 15 minuti

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

modifica: Avevo un sacco di collegamenti aperti e ho pensato che fosse specifico per osx, mi dispiace per quello. Lasciando la mia risposta in modo che altri siano consapevoli del congedo per i BSD.


1
Leave funziona anche in Linux, ma prima devi installare il programma Leave dai repository predefiniti.
karel

6

Questo è per un cronometro con centesimi di secondo:

#!/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
  }
}

Esempio


Bella soluzione solo per Linux! Adoro la mancanza di chiamate esterne. Nota, il mio sistema Debian ha due valori in / proc / uptime, con il secondo presumibilmente riferito al mio uptime precedente. Questo script può essere modificato per rimediare modificando la riga 5 inreturn $1
Adam Katz,

4

Ho combinato l'ottima risposta del terdon, in una funzione che mostra contemporaneamente il tempo dall'inizio e il tempo fino alla fine. Esistono anche tre varianti, quindi è più facile da chiamare (non è necessario eseguire calcoli matematici) ed è anche astratto. Esempio di utilizzo :

{ ~ }  » 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

E qualcosa come il timer di lavoro:

{ ~ }  » 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

E se hai bisogno di un tempo molto specifico:

{ ~ }  » 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

Ecco il codice da inserire nel tuo .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 questo con un modo di riprodurre l'audio nel terminale Linux ( riproduci file mp3 o wav tramite riga di comando Linux ) o cygwin ( cat /path/foo.wav > /dev/dsp funziona per me in babun / win7) e hai un semplice timer flessibile con allarme !


4

Un altro approccio

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

Per Mac:

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

Se si desidera un segnale quando colpisce zero, si potrebbe ad esempio costruirlo con un comando che ha restituito uno stato di uscita diverso da zero a zero e combinarlo con watch -b, o qualcosa del genere, ma se si vuole costruire uno script più elaborato, questo è probabilmente non è la strada da percorrere; si tratta più di una soluzione di tipo "one-liner veloce e sporco".


Mi piace il watchprogramma in generale. L'ho visto per la prima volta dopo aver già scritto innumerevoli while sleep 5; doloop con diversi effetti. watchera manifestamente più bello.


3

Ho finito per scrivere il mio script di 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 

1
Bel copione, +1. Solo per sapere che è un conto alla rovescia, non un cronometro.
terdon,

ah hai ragione, questo è quello che volevo davvero. Aggiornerò la mia denominazione.
tir38,

3

Sono sorpreso che nessuno abbia usato lo sleepenhstrumento nei loro script. Al contrario, le soluzioni proposte utilizzano una sleep 1tra le uscite timer successive o un loop occupato che emette il più velocemente possibile. Il primo è inadeguato perché a causa del piccolo tempo impiegato per la stampa, l'output in realtà non avverrà una volta al secondo, ma un po 'meno di quello che non è ottimale. Dopo che è trascorso abbastanza tempo, il contatore salterà un secondo. Quest'ultimo è inadeguato perché mantiene occupata la CPU senza una buona ragione.

Lo strumento che ho nei miei $PATHassomiglia a questo:

#!/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

Lo script può essere utilizzato come cronometro (conteggio fino a quando non viene interrotto) o come timer che viene eseguito per il periodo di tempo specificato. Poiché sleepviene utilizzato il comando, questo script consente di specificare la durata per la quale contare con la stessa precisione sleepconsentita. Su Debian e derivati, questo include dormite sub-seconde e un bel modo leggibile dall'uomo per specificare l'ora. Quindi ad esempio puoi dire:

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

E come puoi vedere, il comando è stato eseguito esattamente per 2 minuti e 4,6 secondi senza molta magia nello script stesso.

MODIFICA :

Lo strumento sleepenh deriva dal pacchetto con lo stesso nome in Debian e dai suoi derivati ​​come Ubuntu. Per le distribuzioni che non lo hanno, proviene da https://github.com/nsc-deb/sleepenh

Il vantaggio di sleepenh è che è in grado di tenere conto del piccolo ritardo che si accumula nel tempo dall'elaborazione di altre cose oltre al sonno durante un ciclo. Anche se uno lo facesse sleep 1in un ciclo solo 10 volte, l'esecuzione complessiva richiederebbe un po 'più di 10 secondi a causa del piccolo sovraccarico derivante dall'esecuzione sleepe dall'iterazione del ciclo. Questo errore si accumula lentamente e col tempo renderebbe il nostro cronometro sempre più impreciso. Per risolvere questo problema, ogni iterazione di loop deve calcolare il tempo preciso di sospensione che di solito è leggermente inferiore a un secondo (per i timer a intervallo di un secondo). Lo strumento sleepenh fa questo per te.


Non capisco quale beneficio questo dia sulla mia risposta che usa anche sleep 0.1. Che cos'è sleepnh(non riesco a trovarlo nei repository Arch) e in cosa differisce sleep? Per quanto ne so, stai praticamente facendo la stessa cosa della mia risposta sopra. Cosa mi sto perdendo?
terdon,

@terdon Sleepenh viene da qui github.com/nsc-deb/sleepenh Il problema con il solo dire sleep 5è che non dormi esattamente per 5 secondi. Prova ad esempio time sleep 5e vedi che eseguire il comando richiede poco più di 5 secondi. Nel tempo gli errori si accumulano. L'utilità sleepenh consente di evitare facilmente questo accumulo di errori.
Jos

OK. Sul mio sistema vedo un errore di 0,002 secondi. Dubito davvero che qualcuno userebbe questo tipo di strumento e si aspetterebbe una precisione migliore di millisecondi, ma sarebbe meglio se almeno modifichi la tua risposta e io) spieghi perché sleepnhè meglio di sleep(dici solo che usano altre risposte sleep 1- che non fanno , solo l'OP lo utilizza) e ii) dove trovarlo e come installarlo poiché non è uno strumento standard.
terdon,

1
@terdon Ho spiegato la differenza tra sleepe sleepenhnel primo paragrafo. Comunque, probabilmente non ero abbastanza chiaro su questo, quindi alla fine mi sono espanso di più. I problemi di precisione in millisecondi sono ciò che si ottiene quando si chiama sleepuna volta. Si accumulano nel tempo e ad un certo punto è evidente. Non ho detto che altri usano solo sleep 1. Ho detto che usano sleep 1o un occupato. Che fanno ancora. Mostrami un contro esempio. Le risposte che fanno sleep 0.1sono le stesse di quelle che fanno sleep 1tranne che accumulano errori ancora più velocemente.
Jos

Ho trovato le risposte cercando qualcuno che riconoscesse almeno l'esistenza del problema che hai risolto.
mariotomo,

2

Risposta breve:

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

Spiegazione:

So che ci sono molte risposte, ma voglio solo pubblicare qualcosa di molto vicino alla domanda di OP, che personalmente accetterei come davvero " conto alla rovescia nel terminale ". I miei obiettivi erano:

  1. Una fodera
  2. Conto alla rovescia.
  3. Facile da ricordare e digitare console (nessuna funzione e logica pesante, solo bash).
  4. Non richiede software aggiuntivo da installare (può essere utilizzato su qualsiasi server tramite ssh, anche se non ho root lì).

Come funziona:

  1. seq stampa numeri da 60 a 1.
  2. echo -ne "\r$i "riporta il punto di inserimento all'inizio della stringa e stampa il $ivalore corrente . Lo spazio dopo è necessario per sovrascrivere il valore precedente, se era più lungo di caratteri rispetto all'attuale $i(10 -> 9).

1
Questo ha funzionato meglio per il mio caso d'uso su Mac OS per l'uso in uno script bash. La spiegazione di come funziona è molto utile.
James Campbell,

1

Per riferimento futuro, esiste uno strumento da riga di comando chiamato µTimer con opzioni di riga di comando molto semplici per un conto alla rovescia / conto alla rovescia.


1

Fai finta di essere una persona su OSX alla ricerca di un cronometro da riga di comando. Fai finta di non voler installare gli strumenti gnu e vuoi solo eseguirlo con unixdate

in quel caso fai come dice @terdon ma con questa modifica:

function stopwatch(){
    date1=`date +%s`; 
    while true; do 
        echo -ne "$(date -jf "%s" $((`date +%s` - $date1)) +%H:%M:%S)\r"; 
        sleep 0.1
    done
}

1
L'ho provato su OS X El Capitan, per qualche motivo inizia alle 16:00:00
nopole,

1

Basta usare watch + date in ora UTC. Puoi anche installare alcuni pacchetti per display di grandi dimensioni ...

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'

Provalo!

Vedi anche http://www.cyberciti.biz/faq/create-large-colorful-text-banner-on-screen/


1

sw è un semplice cronometro che funzionerà per sempre.

sw

Installare

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

1

Un esempio di 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()

dimostrazione


1

Dai un'occhiata a TermTime , è un bel orologio e cronometro basato su terminali:

pip install termtime


0

Questo è simile alla risposta accettata, ma terdon countdown()mi ha dato errori di sintassi. Questo funziona alla grande per me, però:

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!"; }

Puoi inserirlo .bashrce quindi eseguire con: timer t(dove t è il tempo in minuti).


0

Oggi ho trovato questa domanda prima, quando cercavo un'applicazione a termine per visualizzare un grande conto alla rovescia per un seminario. Nessuno dei suggerimenti era esattamente quello di cui avevo bisogno, quindi ne ho rapidamente messo un altro in Go: https://github.com/bnaucler/cdown

Poiché la domanda ha già una risposta sufficiente, considera che ciò è nell'interesse dei posteri.



0

Una versione GUI del cronometro

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

-2

Se desideri un programma compilabile per qualsiasi motivo, funzionerebbe quanto segue:

#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;
}

Questo può essere usato anche in altri programmi e facilmente trasferito, se un ambiente bash non è disponibile o preferisci semplicemente usare un programma compilato

github

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.