Есть ли способ отображения таймера обратного отсчета или секундомера в терминале?

144

Как я могу отобразить таймер обратного отсчета в реальном времени на терминале Linux? Существует ли существующее приложение или, что еще лучше, один вкладыш для этого?

tir38
источник

Ответы:

184

Я не уверен, зачем вам beep, если все, что вам нужно, это секундомер, вы можете сделать это:

while true; do echo -ne "`date`\r"; done

Это покажет вам секунды, проходящие в режиме реального времени, и вы можете остановить его с помощью Ctrl+ C. Если вам нужна большая точность, вы можете использовать это, чтобы дать вам наносекунды:

while true; do echo -ne "`date +%H:%M:%S:%N`\r"; done

Наконец, если вам действительно очень нужен «формат секундомера», когда все начинается с 0 и начинает расти, вы можете сделать что-то вроде этого:

date1=`date +%s`; while true; do 
   echo -ne "$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r";
done

Для таймера обратного отсчета (это не то, о чем просил ваш первоначальный вопрос) вы могли бы сделать это (измените секунды соответственно):

seconds=20; date1=$((`date +%s` + $seconds)); 
while [ "$date1" -ge `date +%s` ]; do 
  echo -ne "$(date -u --date @$(($date1 - `date +%s` )) +%H:%M:%S)\r"; 
done

Вы можете объединить их в простые команды, используя функции bash (или любой другой оболочки). В bash добавьте эти строки к себе ~/.bashrc(это sleep 0.1заставит систему ждать 1/10-ю секунды между каждым запуском, чтобы не спамить ваш процессор):

function countdown(){
   date1=$((`date +%s` + $1)); 
   while [ "$date1" -ge `date +%s` ]; do 
     echo -ne "$(date -u --date @$(($date1 - `date +%s`)) +%H:%M:%S)\r";
     sleep 0.1
   done
}
function stopwatch(){
  date1=`date +%s`; 
   while true; do 
    echo -ne "$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r"; 
    sleep 0.1
   done
}

Затем вы можете запустить таймер обратного отсчета на одну минуту, запустив:

countdown 60

Вы можете отсчитывать два часа с:

countdown $((2*60*60))

или целый день, используя:

countdown $((24*60*60))

И запустите секундомер, запустив:

stopwatch

Если вам нужно иметь дело с днями, а также с часами, минутами и секундами, вы можете сделать что-то вроде этого:

countdown(){
    date1=$((`date +%s` + $1));
    while [ "$date1" -ge `date +%s` ]; do 
    ## Is this more than 24h away?
    days=$(($(($(( $date1 - $(date +%s))) * 1 ))/86400))
    echo -ne "$days day(s) and $(date -u --date @$(($date1 - `date +%s`)) +%H:%M:%S)\r"; 
    sleep 0.1
    done
}
stopwatch(){
    date1=`date +%s`; 
    while true; do 
    days=$(( $(($(date +%s) - date1)) / 86400 ))
    echo -ne "$days day(s) and $(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r";
    sleep 0.1
    done
}

Обратите внимание, что stopwatchфункция не тестировалась в течение нескольких дней, так как я не хотел ждать ее 24 часа. Это должно работать, но, пожалуйста, дайте мне знать, если это не так.

terdon
источник
9
Я добавил эти приятные функции .zshrcсразу после прочтения вашего ответа. Сегодня я использовал countdownпервый раз и заметил довольно высокую загрузку процессора. Я добавил sleep 0.1(я не знаю, поддерживается ли время ожидания доли секунды во всех системах), что значительно улучшило это. Недостатком является, конечно, менее точная точность отображения, но я могу жить с отклонением макс. 100мс.
mpy
@ Спасибо за упоминание этого. Я получаю ~ 3% загрузки процессора при выполнении этого в bash, я могу жить с этим (хотя это больше , чем я ожидал , и я также добавил сон моей .bashrc).
тердон
3
Просто нашел этот ответ. Я обнаружил, что помещать возврат каретки в начало оператора echo в функции секундомера было удобно, поскольку это означало, что уничтожение секундомера не переписывает текущее время секундомера: echo -ne "\ r $ (date -u --date @ $ (( date +%s- $ date1)) +% H:% M:% S) ";
mkingston
3
@chishaku: В OS X мне показалось, что это работает: echo -ne "$(date -ju -f %s $(($date1 - date +% s )) +%H:%M:%S)\r";
user1071847
1
С пакетом Сокс Я хотел бы добавить хороший звук , чтобы играть в конце обратного отсчета: play -q -n synth 2 pluck C5.
Пабло А
94

Мой любимый способ это:

Начало:

time cat

Стоп:

ctrl+c

Как прокомментировал @wjandrea ниже, запускается еще одна версия:

time read

и нажмите, Enterчтобы остановить

alfasin
источник
8
похоже, но вы можете нажать Enter, чтобы остановить его:time read
wjandrea
10
time readтерпит неудачу в zsh, но time (read)работает.
Sparhawk
Проблема, однако, в том, что вы не можете увидеть время, не отменив его или не закончив его Когда вы затем перезапустите его, таймер начнет заново.
Зельфир Кальцталь
39

Я искал то же самое и закончил тем, что написал что-то более сложное в Python:

Это даст вам простой 10-секундный отсчет:

sudo pip install termdown
termdown 10

Источник: https://github.com/trehn/termdown.

trehn
источник
1
@DoktoroReichard: Ну, это ссылка для скачивания.
harrymc
1
@Suhaib: это должно и делает для меня. Пожалуйста, поднимите вопрос о GitHub с дополнительной информацией.
трин
1
Я люблю это - это прямо в моем переулке
Уэйн Вернер
1
Очень хорошо, мне нравится ASCII
Гильермо
1
Это действительно круто! Or
Орширо
13

Я использовал это:

countdown()
(
  IFS=:
  set -- $*
  secs=$(( ${1#0} * 3600 + ${2#0} * 60 + ${3#0} ))
  while [ $secs -gt 0 ]
  do
    sleep 1 &
    printf "\r%02d:%02d:%02d" $((secs/3600)) $(( (secs/60)%60)) $((secs%60))
    secs=$(( $secs - 1 ))
    wait
  done
  echo
)

Пример:

 countdown "00:07:55"

Вот источник .

саман
источник
2
POSIX-совместимый - работает на OS X :)
djule5
13
sh-3.2# man leave

установить таймер на 15 минут

sh-3.2# leave +0015
Alarm set for Thu Nov  3 14:19:31 CDT 2016. (pid 94317)
sh-3.2#

редактировать: у меня было открыто несколько ссылок, и я подумал, что это специфично для OSX, извините за это. Оставляя мой ответ, чтобы другие знали о выходе на BSD.

ЭФК
источник
1
В Linux Linux работает и работает, но сначала вам нужно установить программу выхода из репозиториев по умолчанию.
Карел
6

Это для секундомера с сотыми долями секунды:

#!/usr/bin/awk -f
function z() {
  getline < "/proc/uptime"
  close("/proc/uptime")
  return $0
}
BEGIN {
  x = z()
  while (1) {
    y = z()
    printf "%02d:%05.2f\r", (y - x) / 60, (y - x) % 60
  }
}

пример

Стивен Пенни
источник
Отличное решение для Linux! Я люблю отсутствие внешних звонков. Обратите внимание, что моя система Debian имеет два значения в / proc / uptime, причем второе предположительно относится к моему предыдущему времени безотказной работы. Этот сценарий можно настроить, чтобы исправить это, изменив строку 5 наreturn $1
Адам Кац
4

Я объединил очень хороший ответ Тердона в функцию, которая одновременно отображает время с начала и время до конца. Есть также три варианта, так что проще звонить (вам не нужно заниматься bash math), и это тоже абстракция. Пример использования :

{ ~ }  » time_minutes 15
Counting to 15 minutes
Start at 11:55:34     Will finish at 12:10:34
     Since start: 00:00:08     Till end:  00:14:51

И что-то вроде работы таймера:

{ ~ }  » time_hours 8
Counting to 8 hours
Start at 11:59:35   Will finish at 19:59:35
     Since start: 00:32:41     Till end:  07:27:19

И если вам нужно определенное время:

{ ~ }  » time_flexible 3:23:00
Counting to 3:23:00 hours
Start at 12:35:11   Will finish at 15:58:11
     Since start: 00:00:14     Till end:  03:22:46

Вот код, который нужно вставить в ваш .bashrc

function time_func()
{
   date2=$((`date +%s` + $1));
   date1=`date +%s`;
   date_finish="$(date --date @$(($date2)) +%T )"

   echo "Start at `date +%T`   Will finish at $date_finish"

    while [ "$date2" -ne `date +%s` ]; do
     echo -ne "     Since start: $(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)     Till end:  $(date -u --date @$(($date2 - `date +%s`)) +%H:%M:%S)\r";
     sleep 1
    done

    printf "\nTimer finished!\n"
    play_sound ~/finished.wav
}

function time_seconds()
{
  echo "Counting to $1 seconds"
  time_func $1
}

function time_minutes()
{
  echo "Counting to $1 minutes"
  time_func $1*60
}

function time_hours()
{
  echo "Counting to $1 hours"
  time_func $1*60*60
}

function time_flexible()  # accepts flexible input hh:mm:ss
{
    echo "Counting to $1"
    secs=$(time2seconds $1)
    time_func $secs
}

function play_sound()  # adjust to your system
{
    cat $1 > /dev/dsp
}

function time2seconds() # changes hh:mm:ss to seconds, found on some other stack answer
{ 
    a=( ${1//:/ }) 
    echo $((${a[0]}*3600+${a[1]}*60+${a[2]})) 
}

Объедините это с каким-либо способом воспроизведения звука в терминале Linux ( проигрывайте mp3 или wav файл через командную строку Linux ) или Cygwin ( cat /path/foo.wav > /dev/dsp работает для меня в babun / win7), и у вас есть простой гибкий таймер с будильником !

Koshmaar
источник
4

Другой подход

countdown=60 now=$(date +%s) watch -tpn1 echo '$((now-$(date +%s)+countdown))'

Для Mac:

countdown=60 now=$(date +%s) watch -tn1 echo '$((now-$(date +%s)+countdown))'
#no p option on mac for watch

Если кто-то хочет получить сигнал, когда он достигает нуля, он может, например, построить его с помощью команды, которая вернула бы ненулевой статус выхода в ноль, и объединить его с watch -bчем-то, но если вы хотите создать более сложный сценарий, это, вероятно, не путь; это скорее «быстрое и грязное однострочное» решение.


Мне нравится watchпрограмма в целом. Впервые я увидел это после того, как написал множество while sleep 5; doциклов для различных эффектов. watchбыло явно лучше.

Даниэль Андерссон
источник
3

В итоге я написал свой собственный скрипт: github gist

#!/bin/sh
# script to create timer in terminal
# Jason Atwood
# 2013/6/22

# start up
echo "starting timer script ..."
sleep 1 # seconds

# get input from user
read -p "Timer for how many minutes?" -e DURATION
DURATION=$(( $DURATION*60 )) # convert minutes to seconds

# get start time
START=$(date +%s)

# infinite loop
while [ -1 ]; do
clear # clear window

# do math
NOW=$(date +%s) # get time now in seconds
DIF=$(( $NOW-$START ))  # compute diff in seconds
ELAPSE=$(( $DURATION-$DIF ))    # compute elapsed time in seconds
MINS=$(( $ELAPSE/60 ))  # convert to minutes... (dumps remainder from division)
SECS=$(( $ELAPSE - ($MINS*60) )) # ... and seconds

# conditional
if [ $MINS == 0 ] && [ $SECS == 0 ] # if mins = 0 and secs = 0 (i.e. if time expired)
then # blink screen
for i in `seq 1 180`; # for i = 1:180 (i.e. 180 seconds)
do
clear # flash on
setterm -term linux -back red -fore white # use setterm to change background color
echo "00:00 " # extra tabs for visibiltiy

sleep 0.5

clear # flash off
setterm -term linux -default # clear setterm changes from above
echo "00:00" # (i.e. go back to white text on black background)
sleep 0.5
done # end for loop
break   # end script

else # else, time is not expired
echo "$MINS:$SECS"  # display time
sleep 1 # sleep 1 second
fi  # end if
done    # end while loop 
tir38
источник
1
Хороший сценарий +1. Просто чтобы вы знали, это таймер обратного отсчета, а не секундомер.
Тердон
ты прав, это то, чего я действительно хотел. Я обновлю свое имя.
tir38
3

Я удивлен, что никто не использовал этот sleepenhинструмент в своих сценариях. Вместо этого в предлагаемых решениях используется либо sleep 1между последующими выходами таймера, либо занятая петля, которая выводит как можно быстрее. Первое неадекватно, потому что из-за небольшого времени, затрачиваемого на печать, вывод на самом деле будет происходить не раз в секунду, а немного меньше, чем тот, который является неоптимальным. По прошествии достаточного времени счетчик пропустит секунду. Последнее неадекватно, потому что оно не загружает процессор без веской причины.

Инструмент, который у меня есть в моей $PATHвыглядит следующим образом:

#!/bin/sh
if [ $# -eq 0 ]; then
    TIMESTAMP=$(sleepenh 0)
    before=$(date +%s)
    while true; do
        diff=$(($(date +%s) - before))
        printf "%02d:%02d:%02d\r" $((diff/3600)) $(((diff%3600)/60)) $((diff%60))
        TIMESTAMP=$(sleepenh $TIMESTAMP 1.0);
    done
    exit 1 # this should never be reached
fi
echo "counting up to $@"
"$0" &
counterpid=$!
trap "exit" INT TERM
trap "kill 0" EXIT
sleep "$@"
kill $counterpid

Сценарий может использоваться как секундомер (считая до прерывания) или как таймер, который работает в течение указанного промежутка времени. Поскольку sleepкоманда используется, этот скрипт позволяет указать длительность, с которой следует считать, с той же точностью, что и ваша sleep. В Debian и его производных это включает в себя спящие секунды, а также удобный способ определения времени. Так, например, вы можете сказать:

$ time countdown 2m 4.6s
countdown 2m 4.6s  0.00s user 0.00s system 0% cpu 2:04.60 total

И, как вы можете видеть, команда выполнялась ровно за 2 минуты и 4,6 секунды без особой магии в самом скрипте.

РЕДАКТИРОВАТЬ :

Инструмент sleepenh происходит из одноименного пакета в Debian и его производных, таких как Ubuntu. Для дистрибутивов, у которых его нет, он приходит по адресу https://github.com/nsc-deb/sleepenh.

Преимущество sleepenh заключается в том, что он способен учитывать небольшую задержку, которая накапливается со временем при обработке других вещей, кроме сна во время цикла. Даже если один sleep 1цикл будет выполнен 10 раз, общее выполнение займет чуть больше 10 секунд из-за небольших накладных расходов, возникающих при выполнении sleepи повторении цикла. Эта ошибка медленно накапливается и со временем сделает наш секундомер все более и более неточным. Чтобы решить эту проблему, необходимо, чтобы каждая итерация цикла вычисляла точное время ожидания, которое обычно немного меньше секунды (для таймеров с интервалом в одну секунду). Инструмент Sleepenh делает это для вас.

josch
источник
Я не понимаю, какую пользу это дает по сравнению с моим ответом, который также использует sleep 0.1. Что такое sleepnh(я не могу найти его в репозиториях Arch) и чем оно отличается sleep? Насколько я могу судить, вы делаете то же самое, что и мой ответ выше. Что мне не хватает?
тердон
@terdon Sleepenh приходит отсюда github.com/nsc-deb/sleepenh Проблема с тем sleep 5, чтобы просто сказать , что вы не спите ровно 5 секунд. Попробуйте, например, time sleep 5и вы увидите, что выполнение команды занимает чуть более 5 секунд. Со временем ошибки накапливаются. Утилита sleepenh позволяет легко избежать накопления ошибок.
Йош
ХОРОШО. В моей системе я вижу ошибку 0,002 секунды. Я действительно сомневаюсь, что кто-то использовал бы такой инструмент и ожидал бы точности, превышающей миллисекунду, но было бы лучше, если бы вы хотя бы отредактировали свой ответ и я) объяснили, почему sleepnhэто лучше, чем sleep(вы говорите, что другие ответы используют - а sleep 1они этого не делают). , только OP использует это) и ii) где его взять и как установить, так как это не стандартный инструмент.
тердон
1
@terdon Я объяснил разницу между sleepи sleepenhв первом абзаце. В любом случае, я, вероятно, не был достаточно ясен в этом вопросе, поэтому я остановился на этом подробнее. Проблемы с точностью до миллисекунды - это то, что вы получаете, когда звоните sleepодин раз. Они накапливаются со временем, и в какой-то момент это заметно. Я не сказал, что другие используют только sleep 1. Я сказал, что они используют sleep 1или busyloop. Что они до сих пор делают. Покажите мне встречный пример. Ответы sleep 0.1такие же, как и ответы, sleep 1за исключением того, что они накапливают ошибки еще быстрее.
17
Я нашел ответы, ища кого-то, по крайней мере признающего существование проблемы, которую вы решили.
Мариотомо
2

Краткий ответ:

for i in `seq 60 -1 1` ; do echo -ne "\r$i " ; sleep 1 ; done

Объяснение:

Я знаю, что есть много ответов, но я просто хочу опубликовать что-то очень близкое к вопросу ОП, которое лично я бы принял как « обратный отсчет в терминале ». Моими целями были:

  1. Один лайнер.
  2. Обратный отсчет.
  3. Легко запомнить и набрать в консоли (без функций и тяжелой логики, только bash).
  4. Не требует установки дополнительного программного обеспечения (может использоваться на любом сервере, на котором я захожу через ssh, даже если у меня там нет рута).

Как это устроено:

  1. seq печатает номера от 60 до 1.
  2. echo -ne "\r$i "возвращает курсор в начало строки и печатает текущее $iзначение. Пробел после него требуется перезаписать предыдущее значение, если оно было длиннее символов, чем текущее $i(10 -> 9).
cronfy
источник
1
Это лучше всего подойдет для моего варианта использования в Mac OS для использования в скрипте bash. Объяснение того, как это работает, очень полезно.
Джеймс Кэмпбелл
1

Для дальнейшего использования, есть инструмент командной строки под названием µTimer с очень простыми параметрами командной строки для таймера обратного отсчета / обратного отсчета.

Том
источник
1

Представьте, что вы человек в OSX, ищущий секундомер командной строки. Представьте, что вы не хотите устанавливать инструменты gnu и просто хотите работать с Unixdate

в этом случае делайте так, как говорит @terdon, но с этой модификацией:

function stopwatch(){
    date1=`date +%s`; 
    while true; do 
        echo -ne "$(date -jf "%s" $((`date +%s` - $date1)) +%H:%M:%S)\r"; 
        sleep 0.1
    done
}
joem
источник
1
Я попробовал его на OS X El Capitan, по какой - то причине он , начиная с 16:00:00
nopole
1

Просто используйте часы + дата в UTC времени. Вы также можете установить пакет для большого дисплея ...

export now="`date +%s -u`";
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now ))'

#Big plain characters
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now )) | toilet -f mono12'

#Big empty charaters
watch -n 0,1 'date +%T -u -d @$((`date +%s` - $now )) | figlet -c -f big'

Попробуй!

Смотрите также http://www.cyberciti.biz/faq/create-large-colorful-text-banner-on-screen/

MUY Бельгия
источник
1

sw - простой секундомер, который будет работать вечно.

ЮЗ

устанавливать

wget -q -O - http://git.io/sinister | sh -s -- -u https://raw.githubusercontent.com/coryfklein/sw/master/sw

использование

sw
 - start a stopwatch from 0, save start time in ~/.sw
sw [-r|--resume]
 - start a stopwatch from the last saved start time (or current time if no last saved start time exists)
 - "-r" stands for --resume
Кори Кляйн
источник
1

Пример Python:

#!/usr/bin/python

def stopwatch ( atom = .01 ):
    import time, sys, math

    start = time.time()
    last = start
    sleep = atom/2
    fmt = "\r%%.%sfs" % (int(abs(round(math.log(atom,10))))  if atom<1 else "")
    while True:
        curr = time.time()
        subatom = (curr-last)
        if subatom>atom:
            # sys.stdout.write( "\r%.2fs" % (curr-start))
            sys.stdout.write( fmt % (curr-start))
            sys.stdout.flush()
            last = curr
        else:
            time.sleep(atom-subatom)

stopwatch()

демонстрация

user84207
источник
1

Взгляните на TermTime , это хорошие терминальные часы и секундомер:

pip install termtime

vimist
источник
0

Это похоже на принятый ответ, но тердон countdown()дал мне синтаксические ошибки. Это прекрасно работает для меня, хотя:

function timer() { case "$1" in -s) shift;; *) set $(($1 * 60));; esac; local S=" "; for i in $(seq "$1" -1 1); do echo -ne "$S\r $i\r"; sleep 1; done; echo -e "$S\rTime's up!"; }

Вы можете вставить его, .bashrcа затем выполнить с помощью: timer t(где t - время в минутах).

Энди Форчено
источник
0

Нашел этот вопрос ранее сегодня, когда искал термин приложение для отображения большого таймера обратного отсчета для семинара. Ни одно из предложений не было именно тем, что мне было нужно, поэтому я быстро собрал другое в Go: https://github.com/bnaucler/cdown

Поскольку на этот вопрос уже дан достаточный ответ, считайте, что это ради потомков.

B Nauclér
источник
0

$ сон 1500 && xterm -fg желтый -g 240x80 &

Когда этот большой терминал с желтым текстом подпрыгнет, пора вставать и тянуться!

Примечания: - 1500 секунд = 25-минутный pomodoro - 240x80 = размер терминала с 240 символами и 80 строками. Заполняет экран для меня заметно.

Предоставлено: http://www.linuxquestions.org/questions/linux-newbie-8/countdown-timer-for-linux-949463/.

user79878
источник
0

GUI-версия секундомера

date1=`date +%s`
date1_f=`date +%H:%M:%S____%d/%m`
(
  while true; do 
    date2=$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)
    echo "# started at $date1_f \n$date2"
  done
) |
zenity --progress \
  --title="Stop Watch" \
  --text="Stop Watch..." \
  --percentage=0
Мохамед Сэми
источник
-2

Если по какой-либо причине вам нужна компилируемая программа, сработает следующее:

#include <iostream>
#include <string>
#include <chrono>

int timer(seconds count) {
  auto t1 = high_resolution_clock::now();
  auto t2 = t1+count;
  while ( t2 > high_resolution_clock::now()) {
    std::cout << "Seconds Left:" <<
    std::endl <<
      duration_cast<duration<double>>(count-(high_resolution_clock::now()-t1)).count() << 
    std::endl << "\033[2A\033[K";
    std::this_thread::sleep_for(milliseconds(100));
  }
  std::cout << "Finished" << std::endl;
  return 0;
}

Это также может быть использовано в других программах и легко переносится, если среда bash недоступна или вы просто предпочитаете использовать скомпилированную программу

GitHub

elder4222
источник