220 votes

Y a-t-il un moyen d'afficher un compte à rebours ou un minuteur de chronomètre dans un terminal?

Comment puis-je afficher un minuteur de compte à rebours en temps réel sur le terminal Linux ? Existe-t-il une application existante ou, encore mieux, une seule ligne de code pour le faire ?

11voto

cronfy Points 201

Réponse courte :

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

Explication :

Je sais qu'il y a beaucoup de réponses, mais je veux juste poster quelque chose de très proche de la question de l'OP, que personnellement j'accepterais comme en effet "compte à rebours en une ligne dans le terminal". Mes objectifs étaient :

  1. Une seule ligne.
  2. Compte à rebours.
  3. Facile à mémoriser et à taper dans la console (pas de fonctions ni de logique complexe, seulement bash).
  4. Ne nécessite pas l'installation d'un logiciel supplémentaire (peut être utilisé sur n'importe quel serveur où je vais via ssh, même si je n'ai pas les droits root).

Comment ça fonctionne :

  1. seq affiche les nombres de 60 à 1.
  2. echo -ne "\r$i " ramène le curseur au début de la chaîne et affiche la valeur actuelle de $i. L'espace après est nécessaire pour écraser la valeur précédente, si elle était plus longue en caractères que la valeur actuelle de $i (10 -> 9).

1 votes

Cela a fonctionné le mieux pour mon cas d'utilisation sur Mac OS pour une utilisation dans un script bash. L'explication de son fonctionnement est particulièrement utile.

7voto

Red Tux Points 2074

J'utilise ce petit programme Go :

package main

import (
   "fmt"
   "time"
)

func format(d time.Duration) string {
   mil := d.Milliseconds() % 1000
   sec := int(d.Seconds()) % 60
   min := int(d.Minutes())
   return fmt.Sprintf("%v m %02v s %03v ms", min, sec, mil)
}

func main() {
   t := time.Now()
   for {
      time.Sleep(10 * time.Millisecond)
      s := format(time.Since(t))
      fmt.Print("\r", s)
   }
}

https://github.com/89z/sienna/tree/master/cmd/stopwatch

6voto

Daniel Andersson Points 22765

Une autre approche

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

Pour Mac:

countdown=60 now=$(date +%s) watch -tn1 echo '$((now-$(date +%s)+countdown))'
#pas d'option p sur mac pour watch

Si on veut un signal quand il atteint zéro, on pourrait par exemple le construire avec une commande qui renvoie un statut de sortie non nul à zéro et le combiner avec watch -b, ou quelque chose, mais si on veut construire un script plus élaboré, ce n'est probablement pas la meilleure solution; c'est plus une solution "rapide et sale en une seule ligne".


J'aime le programme watch en général. Je l'ai vu pour la première fois après avoir déjà écrit d'innombrables boucles while sleep 5; do à différents effets. watch était indéniablement plus agréable.

5voto

Koshmaar Points 89

J'ai combiné la très bonne réponse de terdon dans une fonction qui affiche en même temps le temps écoulé depuis le début et le temps restant jusqu'à la fin. Il existe également trois variantes pour faciliter l'appel (vous n'avez pas à faire de calcul Bash), et c'est également abstrait.

Exemple d'utilisation:

{ ~ }  » time_minutes 15
Compte à rebours de 15 minutes
Début à 11:55:34     Fin à 12:10:34
     Depuis le début: 00:00:08     Jusqu'à la fin:  00:14:51

Et quelque chose comme un minuteur de travail:

{ ~ }  » time_hours 8
Compte à rebours de 8 heures
Début à 11:59:35   Fin à 19:59:35
     Depuis le début: 00:32:41     Jusqu'à la fin:  07:27:19

Et si vous avez besoin d'une heure très spécifique:

{ ~ }  » time_flexible 3:23:00
Compte à rebours de 3:23:00 heures
Début à 12:35:11   Fin à 15:58:11
     Depuis le début: 00:00:14     Jusqu'à la fin:  03:22:46

Voici le code à mettre dans votre .bashrc

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

   echo "Début à `date +%T`   Fin à $date_finish"

    while [ "$date2" -ne `date +%s` ]; do
     echo -ne "     Depuis le début: $(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)     Jusqu'à la fin:  $(date -u --date @$(($date2 - `date +%s`)) +%H:%M:%S)\r";
     sleep 1
    done

    printf "\nMinuteur terminé!\n"
    play_sound ~/finished.wav
}

function time_seconds()
{
  echo "Compte à rebours de $1 secondes"
  time_func $1
}

function time_minutes()
{
  echo "Compte à rebours de $1 minutes"
  time_func $1*60
}

function time_hours()
{
  echo "Compte à rebours de $1 heures"
  time_func $1*60*60
}

function time_flexible()  # Accepte une entrée flexible hh:mm:ss
{
    echo "Compte à rebours de $1"
    secs=$(time2seconds $1)
    time_func $secs
}

function play_sound()  # Ajuster à votre système
{
    cat $1 > /dev/dsp
}

function time2seconds() # Convertit hh:mm:ss en secondes. Trouvé dans une autre réponse de Stack Exchange
{ 
    a=( ${1//:/ }) 
    echo $((${a[0]}*3600+${a[1]}*60+${a[2]})) 
}

Combiner cela avec un moyen de jouer du son dans le terminal Linux (Lire un fichier MP3 ou WAV via la ligne de commande Linux) ou Cygwin (cat /path/foo.wav > /dev/dsp fonctionne pour moi dans Babun/Windows 7) et vous avez un minuteur flexible simple avec alarme!

0 votes

Le projet Babun a été arrêté.

4voto

josch Points 717

Je suis surpris que personne n'ait utilisé l'outil sleepenh dans leurs scripts. Au lieu de cela, les solutions proposées utilisent soit un sleep 1 entre les sorties des minuteurs consécutifs, soit une boucle occupée qui produit des sorties aussi rapidement que possible. La première est insuffisante car en raison du peu de temps passé à imprimer, la sortie ne se fera pas effectivement une fois par seconde mais un peu moins que cela, ce qui est suboptimal. Après suffisamment de temps écoulé, le compteur sautera une seconde. La seconde est insuffisante car elle maintient le processeur occupé pour aucune bonne raison.

L'outil que j'ai dans mon $PATH ressemble à ceci :

#!/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 # cela ne doit jamais être atteint
fi
echo "Décompte jusqu'à $@"
"$0" &
counterpid=$!
trap "exit" INT TERM
trap "kill 0" EXIT
sleep "$@"
kill $counterpid

Le script peut être utilisé comme chronomètre (décompte jusqu'à ce qu'il soit interrompu) ou comme minuterie qui fonctionne pendant la durée spécifiée. Comme la commande sleep est utilisée, ce script permet de spécifier la durée pendant laquelle compter avec la même précision que votre commande sleep le permet. Sur Debian et dérivés, cela comprend les attentes sous-secondes et une manière agréable, lisible par un humain, de spécifier le temps. Donc, par exemple, vous pouvez dire :

$ time countdown 2m 4.6s
compte à rebours de 2m 4.6s  0.00s utilisateur 0.00s système 0% cpu 2:04.60 total

Et comme vous pouvez le voir, la commande a fonctionné exactement pendant 2 minutes et 4,6 secondes sans trop de magie dans le script lui-même.

ÉDITER:

L'outil sleepenh provient du paquet du même nom dans Debian et ses dérivés comme Ubuntu. Pour les distributions qui ne l'ont pas, il vient de https://github.com/nsc-deb/sleepenh

L'avantage de sleepenh est qu'il est capable de prendre en compte le petit retard qui s'accumule au fil du temps à partir du traitement d'autres choses que du sommeil pendant une boucle. Même si l'on ne ferait que sleep 1 dans une boucle 10 fois, l'exécution globale prendrait un peu plus de 10 secondes en raison du petit surcoût provenant de l'exécution de sleep et de l'itération de la boucle. Cette erreur s'accumule lentement et rendrait notre minuterie de chronomètre de plus en plus imprécise au fil du temps. Pour résoudre ce problème, il faut à chaque itération de la boucle calculer le temps précis de sommeil, qui est généralement légèrement inférieur à une seconde (pour des minuteurs d'intervalle d'une seconde). L'outil sleepenh le fait pour vous.

0 votes

Je ne comprends pas quel avantage cela apporte par rapport à ma réponse qui utilise également sleep 0.1. Qu'est-ce que sleepnh (je ne le trouve pas dans les dépôts Arch) et en quoi diffère-t-il de sleep? Autant que je sache, vous faites essentiellement la même chose que ma réponse ci-dessus. Qu'est-ce que je rate?

0 votes

@terdon Sleepenh vient d'ici github.com/nsc-deb/sleepenh Le problème avec simplement dire sleep 5 est que vous ne dormez pas exactement pendant 5 secondes. Essayez par exemple time sleep 5 et vous verrez que l'exécution de la commande prend légèrement plus de 5 secondes. Au fil du temps, les erreurs s'accumulent. L'utilitaire sleepenh permet d'éviter facilement cette accumulation d'erreurs.

0 votes

OK. Sur mon système, je vois une erreur de 0,002 secondes. Je doute vraiment que quelqu'un utilise ce genre d'outil en s'attendant à une précision meilleure que la milliseconde, mais ce serait mieux si vous éditiez au moins votre réponse et i) expliquiez pourquoi sleepnh est meilleur que sleep (vous dites seulement que d'autres réponses utilisent sleep 1 - ce n'est pas vrai, seul l'auteur de la question utilise cela) et ii) où le trouver et comment l'installer car ce n'est pas un outil standard.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X