Comment afficher et mettre à jour l'écho sur la même ligne

112

J'ai ce qui suit dans Bash (sous Linux)

for dir in Movies/*
do
  (cd "$dir" && pwd|cut -d \/ -f5|tr -s '\n' ', ' >> ../../movielist &&
  exiftool * -t -s3 -ImageSize -FileType|tr -s '\t' ',' >> ../../movielist )
echo "Movie $movies - $dir ADDED!"
let movies=movies+1
done

Mais je souhaite faire en sorte que "l'écho" montre l'écho suivant sur la ligne suivante (ne pas concaténer avec la dernière sortie d'écho mais le remplacer) afin de donner l'impression qu'il est en cours de mise à jour. Similaire à la façon dont une barre de progression avec un pourcentage apparaîtrait sur la même ligne.

Luis Alvarado
la source

Réponses:

196

Eh bien, je n'ai pas lu correctement la man echopage pour cela.

echo avait 2 options qui pourraient faire cela si j'ajoutais un troisième caractère d'échappement.

Les 2 options sont -net -e.

-nne sortira pas la nouvelle ligne de fin. Cela m'évite donc de passer à une nouvelle ligne à chaque fois que je fais écho à quelque chose.

-e me permettra d'interpréter les symboles d'échappement anti-slash.

Devinez ce symbole évasion Je veux utiliser pour cela: \r. Oui, le retour chariot me renverrait au départ et il semblerait que je mette à jour sur la même ligne.

Ainsi, la ligne d'écho ressemblerait à ceci:

echo -ne "Movie $movies - $dir ADDED!"\\r

J'ai dû échapper au symbole de fuite pour que Bash ne le tue pas. c'est pourquoi vous voyez 2 \symboles là-dedans.

Comme mentionné par William, printfpeut également effectuer des tâches similaires (et même plus étendues) comme celle-ci.

Luis Alvarado
la source
12
Juste une note pour le futur: printf fera exactement la même chose, sans aucune option. L'avantage est que printf se comporte généralement de la même manière dans tous les environnements et OS, tandis que l'écho peut parfois se comporter très différemment. Pour les scripts multiplateformes (ou si vous pensez que cela vous intéressera peut-être), l'utilisation de printf est la meilleure pratique.
William T Froggard
8
printf a; printf bsorties ab--- printf a\\r; printf bsorties b--- printf a\\r; sleep 1; printf bsorties a, puisb
XavierStuvw
64

Si j'ai bien compris, vous pouvez le faire remplacer votre écho par la ligne suivante:

echo -ne "Movie $movies - $dir ADDED! \033[0K\r"

Voici un petit exemple que vous pouvez exécuter pour comprendre son comportement:

#!/bin/bash
for pc in $(seq 1 100); do
    echo -ne "$pc%\033[0K\r"
    sleep 1
done
echo
arutaku
la source
Il semble que sur zsh le Kn'est pas nécessaire et en fait est publié comme la lettre K.
mknaf
1
Excellent exemple - pour moi le hash bang au début était la clé
Felix Eve
22

Le reste des réponses est plutôt bon, mais je voulais juste ajouter des informations supplémentaires au cas où quelqu'un viendrait ici à la recherche d'une solution pour remplacer / mettre à jour un écho multiligne.

Je voudrais donc partager un exemple avec vous tous. Le script suivant a été essayé sur un système CentOS et utilise la commande "timedatectl" qui imprime essentiellement des informations détaillées sur l'heure de votre système.

J'ai décidé d'utiliser cette commande car sa sortie contient plusieurs lignes et fonctionne parfaitement pour l'exemple ci-dessous:

#!/bin/bash
while true; do
  COMMAND=$(timedatectl) #Save command result in a var.
  echo "$COMMAND" #Print command result, including new lines.

  sleep 3 #Keep above's output on screen during 3 seconds before clearing it

  #Following code clears previously printed lines
  LINES=$(echo "$COMMAND" | wc -l) #Calculate number of lines for the output previously printed
  for (( i=1; i <= $(($LINES)); i++ ));do #For each line printed as a result of "timedatectl"
    tput cuu1 #Move cursor up by one line
    tput el #Clear the line
  done

done

Ce qui précède affichera le résultat de " timedatectl" pour toujours et remplacera l'écho précédent par des résultats mis à jour.

Je dois mentionner que ce code n'est qu'un exemple, mais peut-être pas la meilleure solution pour vous en fonction de vos besoins. Une commande similaire qui ferait presque la même chose (au moins visuellement) est " watch -n 3 timedatectl".

Mais c'est une autre histoire. :)

J'espère que cela pourra aider!

Antony Fuentes Artavia
la source
3

Cela peut être utile, essayez-le et modifiez-le si nécessaire.

#! bin/bash
for load in $(seq 1 100); do
    echo -ne "$load % downloded ...\r"
    sleep 1
done
echo "100"
echo "Loaded ..."
Mantu
la source
3

Vous pouvez essayer ceci .. Ma propre version de celui-ci ..

funcc() {
while true ; do 
for i in \| \/ \- \\ \| \/ \- \\; do 
  echo -n -e "\r$1  $i  "
sleep 0.5
done  
#echo -e "\r                                                                                      "
[ -f /tmp/print-stat ] && break 2
done
}

funcc "Checking Kubectl" & &>/dev/null
sleep 5
touch /tmp/print-stat
echo -e "\rPrint Success                  "
Raghu K
la source
1

Ma méthode préférée s'appelle do the sleep to 50. ici, la ivariable doit être utilisée dans les instructions echo.

for i in $(seq 1 50); do
  echo -ne "$i%\033[0K\r"
  sleep 50
done
echo "ended"
Stéphane
la source
c'est bien, mais le curseur masque le premier caractère. Je suppose que certains espaces dans l'écho pourraient contourner cela.
marathon