Come mostrare e aggiornare l'eco sulla stessa riga


112

Ho quanto segue in Bash (in 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

Ma desidero fare in modo che "echo" mostri il seguente eco sulla riga successiva (non concatenare con l'ultimo echo output ma sostituirlo) in modo da far sembrare che si stia aggiornando. Simile a come verrà visualizzata una barra di avanzamento con percentuale sulla stessa riga.

Risposte:


196

Beh, non ho letto correttamente la man echopagina per questo.

echo aveva 2 opzioni che potevano farlo se avessi aggiunto un terzo carattere di escape.

Le 2 opzioni sono -ne -e.

-nnon produrrà la nuova riga finale. Quindi questo mi evita di passare a una nuova riga ogni volta che faccio eco a qualcosa.

-e mi permetterà di interpretare i simboli di escape backslash.

Indovinate un po 'il simbolo di fuga che voglio utilizzare per questo: \r. Sì, il ritorno a capo mi rimanda all'inizio e visivamente sembrerà che mi sto aggiornando sulla stessa riga.

Quindi la linea dell'eco sarebbe simile a questa:

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

Ho dovuto sfuggire al simbolo di fuga in modo che Bash non lo uccidesse. ecco perché vedi 2 \simboli lì dentro.

Come accennato da William, printfpuò anche svolgere attività simili (e anche più estese) come questa.


12
Solo una nota per il futuro: printf farà esattamente la stessa cosa, senza alcuna opzione. Il vantaggio è che printf generalmente si comporta in modo simile in ogni ambiente e sistema operativo, mentre echo a volte può comportarsi in modo molto diverso. Per gli script multipiattaforma (o se pensi che ti potrebbe interessare), usare printf è la migliore pratica.
William T Froggard

8
printf a; printf buscite ab--- printf a\\r; printf buscite b--- printf a\\r; sleep 1; printf buscite a, quindib
XavierStuvw

64

Se ho capito bene, puoi ottenerlo sostituendo il tuo eco con la seguente riga:

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

Ecco un piccolo esempio che puoi eseguire per comprenderne il comportamento:

#!/bin/bash
for pc in $(seq 1 100); do
    echo -ne "$pc%\033[0K\r"
    sleep 1
done
echo

Sembra che su zsh Knon sia necessario e infatti viene pubblicato come lettera K.
mknaf

1
Ottimo esempio - per me l'hash bang all'inizio è stato fondamentale
Felix Eve

22

Il resto delle risposte è abbastanza buono, ma volevo solo aggiungere alcune informazioni extra nel caso qualcuno arrivasse qui alla ricerca di una soluzione per sostituire / aggiornare un eco multilinea.

Quindi vorrei condividere un esempio con tutti voi. Il seguente script è stato provato su un sistema CentOS e utilizza il comando "timedatectl" che fondamentalmente stampa alcune informazioni dettagliate sull'ora del sistema.

Ho deciso di utilizzare quel comando poiché il suo output contiene più righe e funziona perfettamente per l'esempio seguente:

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

Quanto sopra stamperà il risultato di " timedatectl" per sempre e sostituirà l'eco precedente con i risultati aggiornati.

Devo dire che questo codice è solo un esempio, ma forse non è la soluzione migliore per te a seconda delle tue esigenze. Un comando simile che farebbe quasi lo stesso (almeno visivamente) è " watch -n 3 timedatectl".

Ma questa è una storia diversa. :)

Spero che aiuti!


3

Questo è molto utile, provalo e cambia se necessario.

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

3

Puoi provare questo .. La mia versione di esso ..

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                  "

1

Il mio modo preferito si chiama do the sleep to 50. qui la ivariabile deve essere usata all'interno delle istruzioni echo.

for i in $(seq 1 50); do
  echo -ne "$i%\033[0K\r"
  sleep 50
done
echo "ended"

questo è carino, ma il cursore nasconde il primo carattere. Immagino che alcuni spazi nell'eco potrebbero aggirarlo.
maratona
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.