Comment changer le contenu d'une ligne sur le terminal au lieu d'en écrire une nouvelle?

24

Ainsi, lorsque wgetvous obtenez une page Web, il vous montre une barre d'état qui indique combien de fichiers sont téléchargés. Cela ressemble à ceci:

25%[=============>______________________________________] 25,000 100.0K/s (les traits de soulignement sont des espaces; je n'arrivais tout simplement pas à comprendre comment y insérer plus d'un espace consécutif)

Cependant, au lieu d'écrire une autre ligne sur stdout et d'ajouter une autre barre de progression, il la met à jour, comme ceci:

50%[===========================>________________________] 50,000 100.0K/s

Et ce wgetn'est pas le seul exemple de cela non plus. Par exemple, lorsque vous canalisez quelque chose less, puis quittez, votre invite d'origine est toujours là, avec le résultat des commandes que vous avez exécutées précédemment. C'est comme si tu n'étais jamais parti.

Donc, mes questions sont, comment cela s'appelle-t-il, comment l'implémenter, cela ne fonctionne-t-il que pour une seule ligne à la fois, et puis-je l'utiliser en C?

fouric
la source
5
Je recommande la lecture de BashFAQ 44 . Vous pourriez le trouver intéressant.
jw013

Réponses:

32

Tout d'abord, votre question n'a rien à voir avec bash mais avec le terminal. Le terminal répond pour afficher le texte des programmes et bash lui-même n'a aucun contrôle sur les programmes une fois lancés.

Les terminaux offrent des séquences de contrôle pour contrôler la couleur, la police, la position du curseur et plus encore. Pour une liste des séquences terminales standardisées, consultez http://www.termsys.demon.co.uk/vtansi.htm Vous pouvez par exemple

  • positionnez le curseur au début de la ligne
  • supprimez la ligne après
  • écrire une nouvelle ligne

pour créer une barre de progression.

Les séquences d'échappement de terminal plus avancées dépendent généralement du terminal, par exemple ne fonctionnent qu'avec Eterm ou xterm. ncurses - est une bibliothèque de programmation qui permet de créer des programmes interactifs avec le terminal afin que vous n'ayez pas à utiliser de séquences d'échappement.

Comment écraser une ligne existante avec des séquences de terminaux

echo long text
sleep 1
printf "\033[1A"  # move cursor one line up
printf "\033[K"   # delete till end of line
echo foo

Comment remplacer une ligne existante sans séquence de terminal

Une solution simple consiste à ne pas écrire une nouvelle ligne à la fin mais à écrire un retour chariot, qui réinitialise fondamentalement le curseur au début de la ligne, par exemple:

echo -n first 
sleep 1 
echo -ne "\rsecond"
echo

Le \rretour chariot ou mettra le curseur au début de la ligne et vous permettra d'écraser le contenu de la ligne.

Basculer entre les tampons comme lessouvi

Le comportement de lessest également dû à une fonctionnalité de terminal plus avancée, l'écran alternatif:

En mode VT102, il existe des séquences d'échappement pour activer et désactiver un tampon d'écran alternatif, de la même taille que la zone d'affichage de la fenêtre. Lorsqu'il est activé, l'écran actuel est enregistré et remplacé par l'écran alternatif. L'enregistrement des lignes défilant en haut de la fenêtre est désactivé jusqu'à ce que l'écran normal soit restauré. L'entrée term‐ cap (5) pour xterm permet à l'éditeur visuel vi (1) de passer à l'écran alternatif pour l'édition et de restaurer l'écran à la sortie. Une entrée de menu contextuel facilite le basculement entre les écrans normal et alterné pour couper et coller.

http://rosettacode.org/wiki/Terminal_control/Preserve_screen donne quelques exemples de la façon de le faire vous-même, soit via tput, soit via des séquences d'échappement.

Ulrich Dangel
la source
15

Au lieu d'utiliser echoqui ajoute automatiquement une nouvelle ligne à la chaîne, utilisez printf "%s\r" whatever- le retour chariot envoie le curseur au début de la ligne actuelle. Exemple:

seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""
glenn jackman
la source
selon le curseur de votre terminal, il peut être plus agréable de le faireprintf "\r%2d " $num
glenn jackman