web-dev-qa-db-fra.com

Comment supprimer et remplacer la dernière ligne du terminal en utilisant bash?

Je veux implémenter une barre de progression affichant les secondes écoulées dans bash. Pour cela, j'ai besoin d'effacer la dernière ligne affichée à l'écran (la commande "clear" efface tout l'écran, mais je dois effacer uniquement la ligne de la barre de progression et la remplacer par les nouvelles informations).

Le résultat final devrait ressembler à:

$ Elapsed time 5 seconds

Ensuite, après 10 secondes, je veux remplacer cette phrase (dans la même position à l'écran) par:

$ Elapsed time 15 seconds
84
Debugger

faire écho à un retour chariot avec\r

seq 1 1000000 | while read i; do echo -en "\r$i"; done

de l'écho de l'homme:

-n     do not output the trailing newline
-e     enable interpretation of backslash escapes

\r     carriage return
105
Ken

Le retour chariot seul ne déplace le curseur qu'au début de la ligne. C'est OK si chaque nouvelle ligne de sortie est au moins aussi longue que la précédente, mais si la nouvelle ligne est plus courte, la ligne précédente ne sera pas complètement écrasée, par exemple:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

Pour réellement effacer la ligne du nouveau texte, vous pouvez ajouter \033[K après le \r:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r\033[K0123456789"
0123456789

http://en.wikipedia.org/wiki/ANSI_escape_code

172
Derek Veit

La réponse de Derek Veit fonctionne bien tant que la longueur de ligne ne dépasse jamais la largeur du terminal. Si ce n'est pas le cas, le code suivant empêchera la sortie indésirable:

avant que la ligne ne soit écrite pour la première fois, faites

tput sc

qui enregistre la position actuelle du curseur. Maintenant, chaque fois que vous souhaitez imprimer votre ligne, utilisez

tput rc
tput ed
echo "your stuff here"

pour revenir d'abord à la position de curseur enregistrée, puis effacer l'écran du curseur vers le bas et enfin écrire la sortie.

17
Um.

La méthode\033 n'a pas fonctionné pour moi. La méthode\r fonctionne mais elle n'efface rien, place simplement le curseur au début de la ligne. Donc, si la nouvelle chaîne est plus courte que l'ancienne, vous pouvez voir le texte restant à la fin de la ligne. Au final, tput était la meilleure solution. Il a d'autres utilisations en plus du curseur et il est préinstallé dans de nombreuses distributions Linux et BSD, il devrait donc être disponible pour la plupart des utilisateurs de bash.

#/bin/bash
tput sc # save cursor
printf "Something that I made up for this string"
sleep 1
tput rc;tput el # rc = restore cursor, el = erase to end of line
printf "Another message for testing"
sleep 1
tput rc;tput el
printf "Yet another one"
sleep 1
tput rc;tput el

Voici un petit script de compte à rebours pour jouer avec:

#!/bin/bash
timeout () {
    tput sc
    time=$1; while [ $time -ge 0 ]; do
        tput rc; tput el
        printf "$2" $time
        ((time--))
        sleep 1
    done
    tput rc; tput ed;
}

timeout 10 "Self-destructing in %s"
11
lee8oi

Dans le cas où la sortie de progression est multi-lignes, ou si le script aurait déjà imprimé le nouveau caractère de ligne, vous pouvez sauter les lignes avec quelque chose comme:

printf "\033[5A"

ce qui fera sauter le curseur de 5 lignes vers le haut. Ensuite, vous pouvez remplacer tout ce dont vous avez besoin.

Si cela ne fonctionnait pas, vous pourriez essayer printf "\e[5A" ou echo -e "\033[5A", qui devrait avoir le même effet.

Fondamentalement, avec séquences d'échappement vous pouvez contrôler presque tout sur l'écran.

5
Mr. Goferito

Utilisez le caractère de retour chariot:

echo -e "Foo\rBar" # Will print "Bar"
4
Mikael S

Peut y parvenir en plaçant le retour chariot \r.

Dans une seule ligne de code avec printf

for i in {10..1}; do printf "Counting down: $i\r" && sleep 1; done

ou avec echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
1
Akif