Перезаписать предыдущий вывод в 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 ...

Есть ли возможность заменить последнее значение секунды на самое последнее, чтобы результат не создавал большой след, а считал секунды, как в реальном времени на одной позиции? (Надеюсь, вы понимаете, что я имею в виду :))

NES
источник
Возможно, есть способ сделать это с помощью 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!"
aneeshep
источник
2
Удивительно, спасибо большое. Просто одно уведомление для других читателей. Чтобы соответствовать приведенному выше коду, вы должны использовать echo -en "\ b \ b \ b" из-за пробела.
РЭШ
1
+1 Хорошо, но ... ниже 10 он начинает есть сообщение ...
Лепе
1
Да, лучше использовать \r. Смотри мой ответ.
Микель
3
В руководстве по BASH: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Вместо этого используйте POSIX $((expression))или ((-команду. Например, sek=$(( sek - 1 ))или (( sek = sek - 1 ))или (( sek-- )).
Гейра
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!"
Mikel
источник
7

С Bash вы можете использовать специальную переменную SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'
geirha
источник
+1 Хороший ответ, плюс я никогда не знал о СЕКУНДАХ.
Микель
Это работает, но немного странно видеть «sleep .5» в качестве проверенного условия цикла while. Однако мне особенно нравится использование $ SECONDS, потому что оно относится к реальному времени (т. Е. Не к фиксированному промежутку времени между Действия, отнимающие много времени, как в случае сна 1) ... SECONDS Эта переменная увеличивается до количества секунд, прошедших с момента запуска оболочки. (поэтому я, вероятно, не установил бы его в 0 .. просто протестируйте его еще на 60 секунд с момента запуска сценария) .. +1
Peter.O
Я комментирую дальше, только потому, что лично мне интересен способ получения точного coutntdown ... Я проверил $ ​​SECONDS, и кажется, что если он установлен в «0» или нет, он все еще зависит от текущая дробная секунда системы .. т.е. Установка его в «0» может привести к времени 0,99 ... (то же самое верно, если оставить все как есть) .... Таким образом, его лучший средний шанс - быть в пределах 0,5 секунды. .. Просто комментарий (это то, что комментарии для :)
Peter.O
@ fred.bear Ну, вы никогда не получите это точно; какой-то другой процесс может начать загружать процессор и / или ввод-вывод во время обратного отсчета и разрушить любую точность, которой вы пользовались ранее. Что вы можете сделать, так это подождать как минимум X времени и дать приблизительный отсчет времени, если это необходимо. Когда программа говорит «это займет минуту», ожидаете ли вы, что это займет ровно 1 минуту, до миллисекунды? или взять 1 минуту, дать или занять несколько секунд?
Гейра
@geirha ... Да, это изменение не будет иметь значения в 99% случаев, и это здорово, что вы дали мне знать о $ SECONDS ... Я просто нахожу его пределы ... Я играл с вариант вашего сценария, который сообщает о времени окончания, основанном на 0,01-секундном сне (плюс, как вы упомянули, любое внешнее системное отставание), но печатая только при нажатии на секунду, но затем я обнаружил, что первая «секунда» печатает слишком рано (в одном случае, сразу после первых 0,01 с) ... Это все часть моей кривой обучения bash ... Мне нравится ваш ответ, поэтому я более глубоко взглянул на него.
Peter.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

Ctrl + C, чтобы остановить

обычный
источник
Это не отвечает на вопрос о перезаписи текста
Джереми Керр