Questão Existe uma maneira de exibir um cronômetro de contagem regressiva ou cronômetro em um terminal?


Como posso exibir um cronômetro de contagem regressiva em tempo real no terminal do Linux? Existe um aplicativo existente ou, melhor ainda, um forro para fazer isso?


122


origem


em relação ao SU hold: Eu acho que isso se enquadra em "software de computador" sem "pedir uma recomendação de compra ou de produto". Eu não estou pedindo recomendações; Eu estou perguntando se algo existe mesmo. Quando eu inicialmente fiz a pergunta, eu estava antecipando talvez um "sim, há esse script nix embutido". Se eu puder reestruturar a questão, farei. - tir38
Foi o que tentei fazer. Está no tópico após a minha edição, tanto quanto eu estou preocupado e eu votei para reabri-lo. - terdon
@terdon obrigado, vejo como essa é uma pergunta melhor. - tir38


Respostas:


Eu não tenho certeza porque você precisa beep, se tudo que você quer é um cronômetro, você pode fazer isso:

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

Isso mostrará os segundos passando em tempo real e você pode pará-lo com Ctrl+C. Se você precisar de maior precisão, você pode usar isso para dar nanossegundos:

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

Finalmente, se você realmente quer "formato de cronômetro", onde tudo começa em 0 e começa a crescer, você poderia fazer algo assim:

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

Para um temporizador de contagem regressiva (que não é o que sua pergunta original pediu) você poderia fazer isso (mudar segundos de acordo):

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

Você pode combiná-los em comandos simples usando as funções bash (ou qualquer shell que preferir). No bash, adicione essas linhas ao seu ~/.bashrc (a sleep 0.1 fará com que o sistema aguarde 1/10 de segundo entre cada execução, para que você não envie spam à sua CPU):

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
}

Você pode então iniciar uma contagem regressiva de um minuto executando:

countdown 60

Você pode fazer uma contagem regressiva de duas horas com:

countdown $((2*60*60))

ou um dia inteiro usando:

countdown $((24*60*60))

E inicie o cronômetro executando:

stopwatch

Se você precisa ser capaz de lidar com dias, horas, minutos e segundos, você pode fazer algo assim:

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
}

Note que o stopwatch A função não foi testada por dias desde que eu realmente não queria esperar 24 horas por isso. Deve funcionar, mas por favor me avise se isso não acontecer.


159



Eu adicionei essas funções legais ao meu .zshrc logo depois de ler sua resposta. Hoje eu usei countdown a primeira vez e notei um uso de CPU bastante alto. Eu adicionei um sleep 0.1 (Eu não sei se um tempo de sono de um segundo fracionário é suportado em todos os sistemas), o que melhorou muito. A desvantagem é, naturalmente, uma precisão de exibição menos precisa, mas eu posso viver com um desvio de max. 100ms - mpy
@mpy obrigado por mencionar isso. Estou recebendo ~ 3% do uso da CPU ao fazer isso em bash, Eu posso viver com isso (embora é maior do que eu esperava e eu também adicionei o sono ao meu próprio .bashrc). - terdon
@terdon: Devo admitir que usei em uma máquina antiga (2007?) e a CPU subiu para 18% ... caso contrário, eu provavelmente não teria percebido. - mpy
Apenas encontrei esta resposta. Eu encontrei colocando o retorno de carro no início da declaração de eco na função de cronômetro foi útil, pois significava que matar o cronômetro não substituir o tempo de cronômetro atual: echo -ne "\ r $ (date -u --date @ $ ((date +%s - $ date1)) +% H:% M:% S) "; - mkingston
@chishaku: No OS X, isso pareceu funcionar para mim: echo -ne "$(date -ju -f %s $(($date1 - data +% s)) +%H:%M:%S)\r"; - user1071847


Minha maneira favorita é:

Começar:

time cat

Pare:

ctrl+c

Como @wjandrea comentou abaixo, outra versão deve ser executada:

time read

e pressione Enter parar


83



+1. Lifehacker incluiu este truque. - Nathan Arthur
semelhante, mas você pode pressionar Enter para pará-lo: time read - wjandrea
time read falha em zsh, mas time (read) trabalho. - Sparhawk
O problema é que você não pode ver o tempo sem cancelá-lo ou finalizá-lo. Quando você reiniciá-lo, o timer começa novamente. - Zelphir


Eu estava procurando a mesma coisa e acabei escrevendo algo mais elaborado em Python:

Isso lhe dará uma contagem regressiva simples de 10 segundos:

sudo pip install termdown
termdown 10

Fonte: https://github.com/trehn/termdown


28



@DoktoroReichard: Bem, este é um link de download. - harrymc
não funciona com python 3.x - Suhaib
@Shahaib: Deve e faz por mim. Por favor, levante um problema no GitHub com mais informações. - trehn
Eu amo isso - isso é bem no meu beco - Wayne Werner
Muito bom, eu gosto do ASCII - Guillermo


Eu usei este aqui:

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
)

Exemplo:

 countdown "00:07:55"

Aqui está um fonte.


12



Compatível com POSIX - funciona no OS X :) - djule5


sh-3.2# man leave

definir um temporizador por 15 minutos

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

edit: Eu tinha um monte de links abertos, e eu pensei que isso era específico para osx, desculpe por isso. Deixando minha resposta para que os outros saibam licença nos BSDs.


9



deixe trabalhos no Linux também, mas primeiro você tem que instalar o programa leave dos repositórios padrão. - karel


Isto é para um cronômetro com centésimos de segundo:

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

Exemplo


6



Boa solução apenas para Linux! Eu amo a falta de chamadas externas. Note, meu sistema Debian tem dois valores em / proc / uptime, com o segundo presumivelmente referindo-se ao meu uptime anterior. Este script pode ser ajustado para corrigir isso mudando a linha 5 para return $1 - Adam Katz


Outra abordagem

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

Para Mac:

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

Se alguém quiser um sinal quando chegar a zero, pode-se, por exemplo construí-lo com um comando que retornou um status de saída diferente de zero no zero e combiná-lo com watch -b, ou algo assim, mas se alguém quiser construir um roteiro mais elaborado, este provavelmente não é o caminho a seguir; é mais uma solução do tipo "quick and dirty one-liner".


eu gosto de watch programa em geral. Eu vi pela primeira vez depois que eu já tinha escrito inúmeras while sleep 5; do loops para efeitos diferentes. watch foi demonstravelmente mais agradável.


4





Combinei a resposta do muito bom perdão à função que, ao mesmo tempo, exibe o tempo desde o início e o tempo até o fim. Há também três variantes, por isso é mais fácil ligar (você não precisa fazer bash matemática) e também é abstraído. Exemplo de uso:

{ ~ }  » 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

E algo parecido com temporizador de trabalho:

{ ~ }  » 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

E se você precisar de algum tempo muito específico:

{ ~ }  » 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

Aqui está o código para colocar em seu .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]})) 
}

Combine isso com alguma forma de tocando som no terminal linux (reproduzir arquivos mp3 ou wav via linha de comando do Linux) ou cygwin (cat /path/foo.wav > /dev/dsp  funciona para mim em babun / win7) e você tem um temporizador flexível simples com alarme!


3





Acabei escrevendo meu próprio shell script: github gistub

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

3



Bom roteiro, +1. Só para você saber, isso é uma contagem regressiva, não um cronômetro. - terdon
ha você está certo, isso é o que eu realmente queria. Eu atualizarei minha nomeação. - tir38


Estou surpreso que ninguém usou o sleepenh ferramenta em seus scripts. Em vez disso, as soluções propostas usam um sleep 1 entre as saídas do temporizador subsequentes ou um loop ocupado que produz o mais rápido possível. A primeira é inadequada porque devido ao pequeno tempo gasto com a impressão, a saída não acontecerá uma vez por segundo, mas um pouco menos do que a que é sub-ótima. Depois de passar o tempo suficiente, o contador irá pular um segundo. O último é inadequado porque mantém a CPU ocupada sem um bom motivo.

A ferramenta que tenho na minha $PATH se parece com isso:

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

O script pode ser usado como um cronômetro (contando até ser interrompido) ou como um cronômetro executado durante o período de tempo especificado. Desde o sleep comando é usado, este script permite especificar a duração para a qual contar na mesma precisão que o seu sleep permite. No Debian e nos derivados, isso inclui sub-segundos inativos e uma boa maneira legível de especificar a hora. Então, por exemplo, você pode dizer:

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

E como você pode ver, o comando funcionou exatamente por 2 minutos e 4,6 segundos sem muita mágica no próprio script.

EDITAR:

A ferramenta sleepenh vem do pacote com o mesmo nome no Debian e seus derivados como o Ubuntu. Para distribuições que não têm, vem de https://github.com/nsc-deb/sleepenh

A vantagem do sleepenh é que ele é capaz de levar em conta o pequeno atraso que se acumula ao longo do tempo a partir do processamento de outras coisas além do sono durante um loop. Mesmo se alguém apenas sleep 1 em um loop 10 vezes, a execução geral levaria um pouco mais de 10 segundos por causa da pequena sobrecarga que vem da execução sleep e iterando o loop. Esse erro se acumula lentamente e, com o tempo, tornaria o cronômetro mais impreciso. Para corrigir este problema, é necessário que cada iteração de loop calcule o tempo preciso para dormir, que é geralmente um pouco menor que um segundo (para temporizadores de um segundo). A ferramenta do sleepenh faz isso por você.


3



Eu não entendo o benefício que isso dá à minha resposta, que também usa sleep 0.1. O que é sleepnh (Não consigo encontrá-lo no repositório do Arch) e como ele difere de sleep? Tanto quanto eu posso dizer, você está basicamente fazendo a mesma coisa que a minha resposta acima. o que estou perdendo? - terdon
@terdon Sleepenh vem daqui github.com/nsc-deb/sleepenh O problema com apenas dizer sleep 5 é que você não dorme por exatamente 5 segundos. Tente por exemplo time sleep 5 e você vê que a execução do comando leva pouco mais de 5 segundos. Com o tempo, os erros se acumulam. O utilitário sleepenh permite evitar facilmente esse acúmulo de erros. - josch
ESTÁ BEM. No meu sistema, vejo um erro de 0,002 segundos. Eu realmente duvido que alguém usasse esse tipo de ferramenta e esperasse uma precisão melhor que milissegundos, mas seria melhor se você pelo menos editasse sua resposta e eu explicasse por que sleepnh é melhor que sleep (você só diz que outras respostas usam sleep 1- o que eles não fazem, somente o OP usa isso) e ii) onde obtê-lo e como instalá-lo, já que não é uma ferramenta padrão. - terdon
@terdon eu expliquei a diferença entre sleep e sleepenh no primeiro parágrafo. De qualquer forma, eu provavelmente não estava claro o suficiente sobre isso, então eu expandi mais sobre isso no final. Os problemas de precisão em milissegundos são o que você recebe ao chamar sleep uma vez. Eles se acumulam com o tempo e, em algum momento, é perceptível. Eu não disse que os outros só usam sleep 1. Eu disse que eles usam sleep 1 ou um busyloop. Que eles ainda fazem. Mostre-me um contra-exemplo. Respostas que fazem sleep 0.1 são os mesmos que estão fazendo sleep 1 exceto que eles acumulam erros ainda mais rápido. - josch
Eu desci as respostas procurando alguém pelo menos reconhecendo a existência do problema que você resolveu. - mariotomo