Перезапишіть попередній вихід у Bash, а не додаючи його


22

Для таймера bash я використовую цей код:

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

Це дає мені щось подібне

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

Чи є можливість замінити останнє значення секунди на останнє, щоб результат не генерував великий слід, а відлік секунд, як реальний час, в одній позиції? (Сподіваюся, ви зрозуміли, що я маю на увазі :))


Можливо, є спосіб зробити це за допомогою watchкоманди, хоча я не впевнений, як саме це зробити.
AJMansfield

Відповіді:


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
Дивовижно, спасибі велике. Лише одне повідомлення для інших читачів. Для відповідності коду вище, ви повинні використовувати echo -en "\ b \ b \ b" через пробіл.
NES

1
+1 Приємно, але ... нижче 10 він починає їсти повідомлення ...
lepe

1
Так, краще використовувати \r. Дивіться мою відповідь.
Мікель

3
З посібника з bash : The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Використовуйте замість POSIX $((expression))або ((-command. Наприклад , sek=$(( sek - 1 ))чи (( sek = sek - 1 ))або (( sek-- )).
geirha

17

В основному те саме, що відповідь aneeshep, але використовує Return ( \r), а не Backspace ( \b), тому що ми не знаємо, чи буде довжина завжди однаковою, наприклад, коли $sek < 10.

Крім того, ваш перший echoповинен використовувати $sek, а не жорсткий код 60.

Нарешті, відзначте пробіл після ....

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

З bash ви можете використовувати спеціальну змінну SECONDS.

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

+1 Хороша відповідь, плюс я ніколи не знав про SECONDS.
Мікель

Це працює, але трохи дивно бачити "сон .5" як перевірений стан під час циклу. Ховервер Мені особливо подобається використання $ SECONDS, тому що це стосується реального часу (тобто не фіксованого минулого часу між трудомісткі дії, як у режимі сну 1) ... SECONDS Ця змінна розширюється до кількості секунд з моменту запуску оболонки. (тому я, ймовірно, не ставлю його до 0 .. просто перевіряйте його на 60 секунд більше від початку запуску сценарію) +1
Peter.O

Я коментую далі, лише тому, що мене особисто цікавить спосіб отримати точний coutntdown ... Я перевірив $ SECONDS і, здається, що він встановлений на "0" чи ні, він все ще залежить від дробова секунда системи .. тобто. Якщо встановити його на "0", це може призвести до часу 0,99 ... (те ж саме, якщо його залишити так, як є) .... Отже, його найкращий середній шанс - бути лише за 0,5 секунди. .. Просто коментар (ось що стосується коментарів :)
Peter.O

@ fred.bear Ну, ви ніколи не зрозумієте це точно; якийсь інший процес може почати зависання процесора та / або вводу-виводу під час зворотного відліку і зруйнувати будь-яку точність, яку ви мали раніше. Що ви можете зробити, це змусити його чекати принаймні X кількість часу, а також, якщо бажаєте, дати деякий приблизний відлік часу. Коли програма каже: "це піде на хвилину", чи очікуєш, що це займе рівно 1 хвилину, до мілісекунди? або зайняти 1 хвилину, дати або зайняти кілька секунд?
geirha

@geirha ... Так, ця варіація не має значення в 99% випадків, і її чудово ви дали мені знати про $ SECONDS ... Я просто знаходжу її межі ... Я грав з Варіант Вашого сценарію, який повідомляє про час завершення на основі сну .01 сек (плюс, як Ви вже згадували, будь-яке зовнішнє відставання системи), але друкується лише тоді, коли натискає друга, але тоді я виявив, що перша "Друга" надрукується надруковано (в одному випадку, одразу після першого .01 сек) .. Це все є частиною моєї кривої навчання башму ... Мені подобається ваша відповідь, тому я глибше поглянув на це.
Пітер.O

3

Окрім \rабо \bпідходів, можна використовувати \033[2K контрольний символ , який повідомляє терміналу, щоб очистити всю лінію. Перевага цього порівняно з \bтим, що вам не доведеться співставляти номер \bіз кількістю символів, які потрібно видалити, і в порівнянні з \rними не буде символів, що стирчать на екрані, якщо новий рядок коротший за старий один.

Нижче наводиться приклад того, як це можна застосувати до цього питання, і ось приклад відповідної програми для створення результатів, подібних до завантажувальних повідомлень. У цьому конкретному прикладі таймер пропаде, як тільки буде досягнуто 0-ту секунду, і лінію таймера буде замінено на "Готово!" словосполучення.

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

Іншою альтернативою може бути використання dialogкоманди для створення простих діалогів у командному рядку. Діалогове вікно залишатиметься на екрані протягом тривалості таймера та оновлюватиметься циклом, а до моменту його завершення - таймер буде замінено на повідомлення "Готовий! Натисніть, щоб вийти":

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

dialogне існує на mac: /
Рікі Леві

1

Це те, що я придумав, прочитавши тут і трохи більше, один лайнер:

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

Більш зрозумілі:

#!/bin/bash
SEC=101

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

Якщо SEC можна встановити на будь-яке додатне ціле число, і printf подбає про відповідне набивання. Тестували під Ubuntu та cygwin.


1

Домогтися цього можна, розмістивши зворотній перевезення \r.

У єдиному рядку коду це можливо з echo -ne

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

або з printf

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

-2

Таймер зворотного відліку:

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

а "звичайний" секундомір:

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

контроль + c для зупинки


Це не відповідає на запитання про перезапис тексту
Джеремі Керр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.