Zastąp poprzednie dane wyjściowe w Bash zamiast go dołączać

22

Dla bash timera używam tego kodu:

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

To daje mi coś takiego

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

Czy istnieje możliwość zastąpienia ostatniej wartości sekundy najnowszą, aby wynik nie generował dużego śladu, ale odliczanie sekund jak w czasie rzeczywistym w jednej pozycji? (Mam nadzieję, że rozumiesz o co mi chodzi :))

NES
źródło
Może istnieć sposób na wykonanie tego watchpolecenia, chociaż nie jestem pewien, jak to zrobić.
AJMansfield

Odpowiedzi:

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
źródło
2
Niesamowite, wielkie dzięki. Tylko jedno powiadomienie dla innych czytelników. Aby dopasować powyższy kod, musisz użyć echa -en "\ b \ b \ b" ze względu na spację.
NES,
1
+1 Fajnie, ale ... poniżej 10 zaczyna jeść wiadomość ...
lepe 28.01.11
1
Tak, lepiej użyć \r. Zobacz moją odpowiedź.
Mikel
3
Z instrukcji atakujących za: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Zamiast tego użyj POSIX $((expression))lub ((-command. Np sek=$(( sek - 1 ))lub (( sek = sek - 1 ))lub (( sek-- )).
geirha
17

Zasadniczo to samo co odpowiedź aneeshep, ale używa Return ( \r) zamiast Backspace ( \b), ponieważ nie wiemy, czy długość zawsze będzie taka sama, np $sek < 10. Kiedy .

Również twój pierwszy echo powinien używać $sek, a nie na stałe 60.

Na koniec zwróć uwagę na miejsce po ....

#!/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
źródło
7

W bash możesz użyć specjalnej zmiennej SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'
geirha
źródło
+1 Dobra odpowiedź, a ponadto nigdy nie wiedziałem o SEKUNDACH.
Mikel
Działa, ale trochę dziwnie jest widzieć „sen .5” jako sprawdzony warunek pętli while. Jednak szczególnie podoba mi się użycie $ SECONDS, ponieważ dotyczy ono czasu rzeczywistego (tj. Nieokreślonego czasu, który upłynął między czasochłonne działania, jak w przypadku uśpienia 1) ... SECONDS Ta zmienna rozwija się do liczby sekund od uruchomienia powłoki. (więc prawdopodobnie nie
ustawiłbym
Komentuję dalej, tylko dlatego, że jestem osobiście zainteresowany sposobem uzyskania dokładnego efektu ... Testowałem $ SECONDS i wydaje się, że to, czy jest ustawione na „0”, czy nie, wciąż zależy od aktualny ułamek sekundowy systemu .. tj. Ustawienie go na „0” może dać czas 0,99 ... (to samo jest prawdą, jeśli pozostanie niezmienione)… Zatem jego najlepsza średnia szansa wynosi zaledwie 0,5 sekundy. .. Tylko komentarz (po to są komentarze :)
Peter.O
@ fred.bear Cóż, nigdy nie uzyskasz dokładności; jakiś inny proces może zacząć przejmować procesor i / lub we / wy podczas odliczania i zepsuć wszelką wcześniejszą dokładność. Co możesz zrobić, to poczekać przynajmniej X razy i w razie potrzeby podać przybliżone odliczanie. Kiedy program mówi „zajmie to minutę”, czy spodziewasz się, że zajmie to dokładnie 1 minutę, w milisekundach? lub zabrać 1 minutę, dać lub zająć kilka sekund?
geirha
@geirha ... Tak, ta odmiana nie będzie miała znaczenia w 99% przypadków, a to wspaniałe, że uświadomiłeś mi o SEKUNDACH ... Po prostu znajduję jej ograniczenia ... Grałem z odmiana skryptu, która zgłasza czas zakończenia na podstawie snu trwającego 0,01 sekundy (plus, jak wspomniałeś, każde opóźnienie w systemie zewnętrznym), ale drukuje się dopiero po kliknięciu drugiego przycisku, ale potem odkryłem, że pierwsza „sekunda” drukowała się zbyt wcześnie (w jednym przypadku, zaraz po pierwszej .01 sek.) To wszystko jest częścią mojej krzywej uczenia się bash ... Podoba mi się twoja odpowiedź, dlatego przyjrzałem się jej głębiej.
Peter.O
3

Oprócz \rlub \bzbliża się możliwe jest użycie \033[2K znaku kontrolnego , który każe terminalowi wyczyścić całą linię. Zaletą tego \bjest to, że nie musisz dopasowywać liczby\b do liczby znaków, które chcesz usunąć, a w porównaniu do \rnie będzie znaków wystających na ekranie, jeśli nowa linia jest krótsza niż stara jeden.

Poniżej znajduje się przykład, w jaki sposób można go zastosować do tego pytania, a oto przykład pokrewnej aplikacji do tworzenia danych wyjściowych podobnych do komunikatów rozruchowych. W tym konkretnym przykładzie minutnik zniknie po osiągnięciu 0 sekundy, a linia timera zostanie zastąpiona przez „Gotowy!” wyrażenie.

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

Inną alternatywą byłoby użycie dialogpolecenia do tworzenia prostych okien dialogowych w wierszu poleceń. Okno dialogowe pozostanie na ekranie przez czas trwania timera i zaktualizuje się w pętli, a do czasu, gdy się skończy - timer zostanie zastąpiony komunikatem „Gotowe! Naciśnij, aby wyjść” w płynny sposób:

#!/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
źródło
dialognie istnieje na komputerze Mac: /
Ricky Levi
1

Oto, co wymyśliłem po przeczytaniu tutaj i trochę więcej, jeden linijka:

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

Bardziej czytelny:

#!/bin/bash
SEC=101

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

Gdzie SEC można ustawić na dowolną dodatnią liczbę całkowitą, a printf zajmie się odpowiednim dopełnieniem. Testowane pod Ubuntu i cygwin.

Tod
źródło
1

Można to osiągnąć poprzez umieszczenie zwrotu karetki \r.

W jednym wierszu kodu jest to możliwe za pomocą echo -ne

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

lub z printf

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

Minutnik:

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

a „normalny” stoper to:

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

Ctrl + C, aby zatrzymać

Zwykły
źródło
To nie odpowiada na pytanie o nadpisywanie tekstu
Jeremy Kerr