Überschreiben Sie die vorherige Ausgabe in Bash, anstatt sie anzuhängen

22

Für einen Bash-Timer verwende ich diesen Code:

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

Das gibt mir so etwas

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

Gibt es eine Möglichkeit, den letzten Wert der Sekunde durch den neuesten zu ersetzen, sodass die Ausgabe keine große Spur erzeugt, sondern den Sekundencountdown wie eine Echtzeit an einer Position? (Hoffe du verstehst was ich meine :))

NES
quelle
Es könnte eine Möglichkeit geben, dies mit dem watchBefehl zu tun , obwohl ich nicht genau weiß, wie ich es tun soll.
AJMansfield

Antworten:

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!"
aneeshep
quelle
2
Super, vielen Dank. Nur ein Hinweis für die anderen Leser. Um den obigen Code anzupassen, müssen Sie wegen des Leerzeichens echo -en "\ b \ b \ b" verwenden.
NES
1
+1 Schön, aber ... unter 10 beginnt es die Nachricht zu essen ...
Lepe
1
Ja, besser zu bedienen \r. Siehe meine Antwort.
Mikel
3
Von bash Anleitung: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Verwenden Sie stattdessen das POSIX $((expression))oder den ((-Befehl. ZB sek=$(( sek - 1 ))oder (( sek = sek - 1 ))oder (( sek-- )).
Geirha
17

Grundsätzlich das Gleiche wie die Antwort von aneeshep, jedoch wird Return ( \r) anstelle von Backspace ( \b) verwendet, da wir nicht wissen, ob die Länge immer gleich ist, z $sek < 10. B. wann .

Auch Ihre erste echosollte verwenden $sek, nicht hartcodieren 60.

Beachten Sie zum Schluss das Leerzeichen nach dem ....

#!/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!"
Mikel
quelle
7

Mit bash können Sie die spezielle Variable verwenden SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'
geirha
quelle
+1 Gute Antwort, außerdem wusste ich nie über SECONDS Bescheid.
Mikel
Es funktioniert, aber es ist ein bisschen seltsam, wenn man 'sleep .5' als getestete while-loop-Bedingung ansieht. Allerdings mag ich die Verwendung von $ SECONDS besonders, da es sich um Echtzeit handelt (dh nicht um eine festgelegte verstrichene Zeit zwischen zeitaufwendige Aktionen, wie bei sleep 1) ... SECONDS Diese Variable wird um die Anzahl der Sekunden erweitert, die seit dem Start der Shell vergangen sind. (also würde ich es wahrscheinlich nicht auf 0 setzen ..
teste
Ich kommentiere weiter, nur weil ich persönlich an einer Möglichkeit interessiert bin, ein genaues Ergebnis zu erhalten ... Ich habe $ SECONDS getestet und es scheint, dass es immer noch von der Funktion abhängt, ob es auf '0' gesetzt ist oder nicht System der aktuellen Sekundenbruchteil .. dh. Das Setzen auf '0' kann zu einer Zeit von 0,99 führen ... (dasselbe gilt, wenn es unverändert bleibt) .... Die beste Durchschnittswahrscheinlichkeit liegt also bei nur 0,5 Sekunden. .. Nur ein Kommentar (das ist, was Kommentare sind für :)
Peter.O
@ Fred.bear Nun, Sie werden es nie wirklich genau bekommen; Ein anderer Prozess könnte CPU und / oder E / A überlasten, während der Countdown läuft, und die Genauigkeit, die Sie zuvor hatten, ruinieren. Was Sie tun können, ist, es mindestens X Zeit warten zu lassen und, falls gewünscht, einen groben Countdown durchzuführen. Wenn ein Programm sagt "es dauert eine Minute", erwarten Sie es genau 1 Minute, auf die Millisekunde? oder 1 Minute nehmen, ein paar Sekunden geben oder nehmen?
Geirha
@geirha ... Ja, diese Variation spielt in 99% der Fälle keine Rolle, und es ist großartig, dass Sie mich auf $ SECONDS aufmerksam gemacht haben ... Ich finde nur seine Grenzen ... Ich habe mit a gespielt Variation Ihres Skripts, die die Endzeit auf der Grundlage eines Ruhezustands von 0,01 Sekunden (plus, wie Sie bereits erwähnt, einer externen Systemverzögerung) angibt, aber erst beim Klicken auf eine Sekunde gedruckt wird. Dann stellte ich fest, dass die erste Sekunde zu früh gedruckt wurde (in einem Fall kurz nach der ersten 0,01 Sek.) .. Es ist alles Teil meiner Bash-Lernkurve ... Ich mag Ihre Antwort, deshalb habe ich es mir genauer angesehen.
Peter.O
3

Zusätzlich zu \roder \bAnnäherungen ist es möglich, das \033[2K Steuerzeichen zu verwenden , das das Terminal anweist, die gesamte Zeile zu löschen. Der Vorteil im Vergleich zu \bist, dass Sie \bdie Anzahl der Zeichen nicht mit der Anzahl der zu löschenden Zeichen abgleichen müssen und \rdass keine Zeichen auf dem Bildschirm hervorstehen, wenn die neue Zeile kürzer als die alte ist eins.

Unten ist das Beispiel dafür , wie es auf diese Frage angewandt werden, und hier ist ein Beispiel für die zugehörige Anwendung Ausgabe ähnlich Boot - Meldungen zu erstellen. In diesem speziellen Beispiel wird der Timer gelöscht, sobald die 0. Sekunde erreicht ist, und die Timer-Zeile wird durch "Bereit!" Ersetzt. Phrase.

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

Eine andere Alternative wäre, einen dialogBefehl zum Erstellen einfacher Dialoge in der Befehlszeile zu verwenden. Der Dialog bleibt für die Dauer des Timers auf dem Bildschirm und wird mit der Schleife aktualisiert. Wenn der Vorgang abgeschlossen ist, wird der Timer nahtlos durch die Meldung "Ready! Press to exit" ersetzt:

#!/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
Sergiy Kolodyazhnyy
quelle
dialogexistiert nicht auf Mac: /
Ricky Levi
1

Dies ist, was ich nach dem Lesen hier und ein bisschen mehr, die Einzeiler:

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

Besser lesbar:

#!/bin/bash
SEC=101

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

Wobei SEC auf eine beliebige positive Ganzzahl gesetzt werden kann und der printf für eine angemessene Polsterung sorgt. Getestet unter Ubuntu und Cygwin.

Tod
quelle
1

Kann es durch die Platzierung Wagenrücklauf erreichen \r.

In einer einzigen Codezeile ist es mit möglich echo -ne

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

oder mit printf

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

Countdown-Timer:

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

und die "normale" Stoppuhr ist:

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

Strg + c zum Stoppen

Gewohnheit
quelle
Dies beantwortet nicht die Frage zum Überschreiben von Text
Jeremy Kerr