Substitua a saída anterior no Bash em vez de anexá-la


22

Para um cronômetro bash, eu uso este código:

#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek "  
sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

Isso me dá algo assim

One Moment please: 60 59 58 57 56 55 ...

Existe a possibilidade de substituir o último valor de segundo pelo mais recente, para que a saída não gere uma trilha grande, mas a contagem regressiva de segundos seja como um tempo real em uma posição? (Espero que você entenda o que eu quero dizer :))


Pode haver uma maneira de fazer isso com o watchcomando, embora eu não tenha certeza exatamente como fazê-lo.
AJMansfield

Respostas:


14
#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek" 

sleep 1
   sek=$[$sek-1]
   echo -en "\b\b"
done
echo
echo "ready!"

2
Incrível, muito obrigado. Apenas um aviso para os outros leitores. Para ajustar o código acima, você deve usar echo -en "\ b \ b \ b" devido ao espaço.
NES

1
+1 agradável, mas ... abaixo de 10 ele começa a comer a mensagem ...
Lepe

1
Sim, melhor usar \r. Veja minha resposta.
Mikel

3
Do manual do bash: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Use o POSIX $((expression))ou o ((comando Por exemplo, sek=$(( sek - 1 ))ou (( sek = sek - 1 ))ou (( sek-- )).
Geirha #

17

Basicamente, o mesmo que a resposta de aneeshep, mas usa Return ( \r) em vez de Backspace ( \b) porque não sabemos se o comprimento será sempre o mesmo, por exemplo, quando $sek < 10.

Além disso, o seu primeiro echodeve usar $sek, não código fixo 60.

Por fim, observe o espaço após o ....

#!/bin/bash
sek=60
echo "$sek Seconds Wait!"
while [ $sek -ge 1 ]
do
   echo -ne "One Moment please $sek ... \r"
   sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

7

Com o bash, você pode usar a variável especial SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'

+1 Boa resposta, mais eu nunca soube sobre os SEGUNDOS.
Mikel

Funciona, mas é um pouco estranho ver 'sleep .5' como uma condição de loop while testada. Howerver Gosto particularmente do uso de $ SECONDS, porque se refere ao tempo real (isto é, não um tempo decorrido fixo entre ações demoradas, como no modo de suspensão 1) ... SECONDS Essa variável se expande para o número de segundos desde que o shell foi iniciado. (então eu provavelmente não iria configurá-lo para 0 .. apenas testá-lo por 60 segundos mais a partir de quando o script iniciado) .. +1
Peter.O

Estou comentando mais, apenas porque estou pessoalmente interessado em uma maneira de obter uma precisão exata ... Testei $ SECONDS e parece que, se está definido como '0' ou não, ainda depende do segundo fracionário atual do sistema .. ie. Configurá-lo para '0' pode resultar em um tempo de 0,99 ... (o mesmo acontece se for deixado no estado em que se encontra) ... Portanto, sua melhor chance média é de ficar em apenas 0,5 segundo. .. Apenas um comentário (que é o que os comentários são para :)
Peter.O

@ fred.bear Bem, você nunca será realmente preciso; algum outro processo pode começar a monopolizar a CPU e / ou o IO enquanto a contagem regressiva está acontecendo e arruinar a precisão que você teve anteriormente. O que você pode fazer é esperar pelo menos X tempo e, se desejar, faça uma contagem regressiva aproximada. Quando um programa diz "levará um minuto", você espera que demore exatamente 1 minuto, até o milissegundo? ou demorar 1 minuto, mais ou menos alguns segundos?
Geirha #

@geirha ... Sim, essa variação não importa em 99% dos casos, e é ótimo que você me tenha informado dos $ SECONDS ... Estou apenas encontrando seus limites ... Joguei com um variação do seu script que relata o tempo de conclusão com base em um sono de 0,01 segundo (mais, como você mencionou, qualquer atraso do sistema externo), mas apenas imprimindo quando um segundo clica em cima, mas descobri que o primeiro 'segundo' estava sendo impresso muito cedo (em um caso, logo após o primeiro .01 seg.). Tudo faz parte da minha curva de aprendizado do bash ... Gostei da sua resposta, é por isso que tive uma análise mais profunda.
precisa saber é o seguinte

3

Além de \rou \babordagens, é possível usar o \033[2K caractere de controle , que informa ao terminal para limpar toda a linha. A vantagem disso em comparação com \bé que você não precisa corresponder o número \bcom a quantidade de caracteres que deseja excluir e comparado com\r , não haverá caracteres aparecendo na tela se a nova linha for mais curta que a antiga 1.

Abaixo está o exemplo de como ele pode ser aplicado a esta pergunta, e aqui está um exemplo do aplicativo relacionado para criar uma saída semelhante às mensagens de inicialização. Neste exemplo em particular, o cronômetro será desativado assim que o segundo segundo for alcançado e a linha do cronômetro será substituída por "Pronto!" frase.

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    printf "One moment please: %d" "$sek"
    sleep 1
    printf "\r%b" "\033[2K"
done
echo "Ready!"

Outra alternativa seria empregar dialogcomando para criar diálogos simples na linha de comando. A caixa de diálogo permanecerá na tela durante o tempo do cronômetro e será atualizada com o loop e, quando terminar, o cronômetro será substituído pela mensagem "Pronto! Pressione para sair" de uma maneira uniforme:

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    echo "$sek" | dialog --progressbox "Please wait" 10 25
    sleep 1
done
dialog --msgbox "Ready! Press <OK> to finish" 10 25

dialognão existe no mac: /
Ricky Levi

1

Isto é o que eu descobri depois de ler aqui e um pouco mais, o único tópico:

SEC=101;for i in `seq $SEC -1 1`;do printf "\rNext in: %`expr length $SEC`ds" "$i";sleep 1;done;echo

Mais legível:

#!/bin/bash
SEC=101

for i in `seq $SEC -1 1`;do
        printf "\rNext in: %`expr length $SEC`ds" "$i";
        sleep 1;
done
echo

Onde SEC pode ser definido como qualquer número inteiro positivo e o printf cuidará do preenchimento apropriado. Testado no Ubuntu e cygwin.


1

Pode alcançá-lo colocando retorno de carro \r .

Em uma única linha de código, é possível com echo -ne

for i in {60..1}; do echo -ne "One moment please: $i\r" && sleep 1; done

ou com printf

for i in {60..1}; do printf "One moment please: $i\r" && sleep 1; done

-2

Temporizador:

MIN=1 && for i in $(seq $(($MIN*60)) -1 1); do echo -n "$i, "; sleep 1; done; echo -e "nnMessage"

e o cronômetro 'normal' é:

START=$( date +%s ); while true; do CURRENT=$( date +%s ) ; echo $(( CURRENT-START )) ; sleep 1 ; echo -n  ; done

Ctrl + c para parar


Isso não responder à pergunta sobre o texto substituindo
Jeremy Kerr
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.