Como mostrar e atualizar o eco na mesma linha

112

Tenho o seguinte no Bash (no 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

Mas eu desejo fazer com que o "echo" mostre o seguinte eco na próxima linha (não concatenar com a última saída do eco, mas substituí-la) para que pareça que está sendo atualizado. Semelhante a como uma barra de progresso com porcentagem seria exibida na mesma linha.

Luis alvarado
fonte

Respostas:

196

Bem, eu não li corretamente a man echopágina para isso.

echo tinha 2 opções que poderiam fazer isso se eu adicionasse um terceiro caractere de escape.

As 2 opções são -ne -e.

-nnão produzirá a nova linha final. Isso me poupa de ir para uma nova linha cada vez que repito algo.

-e me permitirá interpretar símbolos de escape de barra invertida.

Adivinha o quê símbolo de fuga que eu quero usar para isso: \r. Sim, o retorno de carro me enviaria de volta ao início e visualmente parecerá que estou atualizando na mesma linha.

Portanto, a linha de eco ficaria assim:

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

Eu tinha que escapar do símbolo de fuga para que Bash não o matasse. é por isso que você vê 2 \símbolos ali.

Conforme mencionado por William, printftambém pode realizar tarefas semelhantes (e ainda mais extensas) como esta.

Luis alvarado
fonte
12
Apenas uma nota para o futuro: printf fará exatamente a mesma coisa, sem opções. A vantagem é que printf geralmente se comporta de maneira semelhante em todos os ambientes e sistemas operacionais, enquanto echo às vezes pode se comportar de maneira muito diferente. Para scripts de plataforma cruzada (ou se você acha que pode se preocupar com isso), usar printf é a prática recomendada.
William T Froggard,
8
printf a; printf bsaídas ab--- printf a\\r; printf bsaídas b--- printf a\\r; sleep 1; printf bsaídas a, entãob
XavierStuvw
64

Se eu entendi bem, você pode substituí-lo substituindo seu eco pela seguinte linha:

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

Aqui está um pequeno exemplo que você pode executar para entender seu comportamento:

#!/bin/bash
for pc in $(seq 1 100); do
    echo -ne "$pc%\033[0K\r"
    sleep 1
done
echo
Arutaku
fonte
Parece que em zsh o Knão é necessário e de fato é colocado como a letra K.
mknaf
1
Ótimo exemplo - para mim, o hash bang no início foi fundamental
Felix Eve
22

O resto das respostas são muito boas, mas só queria adicionar algumas informações extras no caso de alguém vir aqui procurando uma solução para substituir / atualizar um eco multilinha.

Então, eu gostaria de compartilhar um exemplo com todos vocês. O seguinte script foi tentado em um sistema CentOS e usa o comando "timedatectl" que basicamente imprime algumas informações detalhadas de tempo de seu sistema.

Decidi usar esse comando porque sua saída contém várias linhas e funciona perfeitamente para o exemplo abaixo:

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

O acima irá imprimir o resultado de " timedatectl" para sempre e irá substituir o eco anterior por resultados atualizados.

Devo mencionar que este código é apenas um exemplo, mas talvez não seja a melhor solução para você dependendo de suas necessidades. Um comando semelhante que faria quase o mesmo (pelo menos visualmente) é " watch -n 3 timedatectl".

Mas essa é uma história diferente. :)

Espero que ajude!

Antony Fuentes Artavia
fonte
3

Isso é útil para variar, tente e altere conforme necessário.

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

Você pode tentar isso .. Minha própria versão disso ..

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
fonte
1

Minha forma favorita é chamada do the sleep to 50. here ivariável precisa ser usada dentro de declarações echo.

for i in $(seq 1 50); do
  echo -ne "$i%\033[0K\r"
  sleep 50
done
echo "ended"
Stphane
fonte
isso é bom, mas o cursor obscurece o primeiro caractere. Acho que alguns espaços no eco podem contornar isso.
maratona de