Existe uma forma de mostrar um contador decrescente ou um cronómetro num terminal?
Como posso exibir um contador decrescente em tempo real no terminal Linux? Existe alguma aplicação existente ou, melhor ainda, um liner para o fazer?
Como posso exibir um contador decrescente em tempo real no terminal Linux? Existe alguma aplicação existente ou, melhor ainda, um liner para o fazer?
Não tenho a certeza porque precisa de beep
, se tudo o que quer é um cronómetro, pode fazer isto:
while true; do echo -ne "`date`\r"; done
Isso mostrar-lhe-á os segundos que passam em tempo real e pode pará-lo com Ctrl+C. Se precisar de maior precisão, pode usar isto para lhe dar nanossegundos:
while true; do echo -ne "`date +%H:%M:%S:%N`\r"; done
Finalmente, se realmente, quiser “formato de cronómetro”, onde tudo começa em 0 e começa a crescer, pode fazer algo como isto:
date1=`date +%s`; while true; do
echo -ne "$(date -u --date @$((`date +%s` - $date1)) +%H:%M:%S)\r";
done
Para um temporizador de contagem decrescente (que não é o que a sua pergunta original lhe pediu) poderia fazer isto (mudar segundos em conformidade):
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
Pode combiná-los em comandos simples, usando funções bash (ou qualquer concha que prefira). Na bash, adicione estas linhas ao seu ~/.bashrc
(o sleep 0.1
fará o sistema esperar 1/10 de segundo entre cada execução para que não faça spam no seu 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
}
Pode então iniciar uma contagem decrescente de um minuto correndo:
countdown 60
Pode contar duas horas com:
countdown $((2*60*60))
ou um dia inteiro de utilização:
countdown $((24*60*60))
E iniciar o cronómetro correndo:
stopwatch
Se precisar de ser capaz de lidar com dias assim como com horas, minutos e segundos, pode fazer algo como isto:
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
}
Nota que a função stopwatch
não foi testada durante dias, uma vez que eu não queria realmente esperar 24 horas por ela. Deve funcionar, mas por favor avisem-me se não funcionar.
sh-3.2# man leave
definir um temporizador para 15 minutos
sh-3.2# leave +0015
Alarm set for Thu Nov 3 14:19:31 CDT 2016. (pid 94317)
sh-3.2#
editar: eu tinha um monte de links abertos, e pensei que isto era específico para osx, lamento por isso. Deixando a minha resposta para que outros estejam cientes da licença nos BSDs.
Já 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á uma fonte .
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
}
}
Resposta curta:
for i in `seq 60 -1 1` ; do echo -ne "\r$i " ; sleep 1 ; done
Explicação:
Sei que há muitas respostas, mas quero apenas colocar algo muito próximo da pergunta da OP, que pessoalmente aceitaria como de facto “ contagem regressiva de um terminal”. Os meus objectivos eram:
Como funciona:
seq
imprime números de 60 a 1. echo -ne "\r$i "
devolve o carpete ao início da corda e imprime o valor actual $i
. Espaço depois é necessário para sobrescrever o valor anterior, se foi mais longo por caracteres do que o actual $i
(10 -> 9).Combinei a muito boa resposta do terdon, em função que ao mesmo tempo mostra o tempo desde o início, e o tempo até ao fim. Existem também três variantes, por isso é mais fácil de chamar (não é preciso fazer contas de bash), e também é abstraído. Exemplo de utilização :
{ ~ } » 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 o tempo 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 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* a colocar no 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 isto com alguma forma de disponibilizar som* no terminal linux play mp3 ou ficheiro wav via linha de comando Linux ) ou cygwin (cat /path/foo.wav > /dev/dsp
funciona para mim em babun/win7) e tem um simples temporizador flexível com alarme*!
Acabei por escrever o meu próprio guião de concha: 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
Surpreende-me que ninguém tenha utilizado a ferramenta sleepenh
nos seus guiões. Em vez disso, as soluções propostas ou utilizam um sleep 1
entre saídas temporizadas subsequentes ou um loop ocupado que sai o mais rápido possível. A primeira é inadequada porque, devido ao pouco tempo gasto na impressão, a saída não irá realmente acontecer uma vez por segundo, mas um pouco menos do que a que é subóptima. Depois de passado tempo suficiente, o contador saltará um segundo. O último é inadequado porque mantém o CPU ocupado sem qualquer razão válida.
A ferramenta que tenho no meu $PATH
parece-se com isto:
#!/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 relógio de paragem (contagem até ser interrompido) ou como um temporizador que funciona durante o período de tempo especificado. Uma vez que o comando sleep
é utilizado, este script permite especificar a duração para a qual contar com a mesma precisão que o seu sleep
permite. Em Debian e derivados, isto inclui o sub-segundo segundo que dorme e uma forma agradável e legível para o ser humano de especificar o tempo. Assim, por exemplo, 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 pode ver, o comando funcionou exactamente durante 2 minutos e 4,6 segundos sem muita magia no próprio guião.
EDIT :
A ferramenta sleepenh vem do pacote com o mesmo nome em Debian e os seus derivados como o Ubuntu. Para distribuições que não o têm, vem de https://github.com/nsc-deb/sleepenh
A vantagem do sleepenh é, que é capaz de ter em conta o pequeno atraso que se acumula ao longo do tempo a partir do processamento de outras coisas que não o sono durante um loop. Mesmo que se fizesse apenas sleep 1
num loop 10 vezes, a execução global levaria um pouco mais de 10 segundos devido à pequena sobrecarga que advém da execução de sleep
e da iteração do loop. Este erro acumula-se lentamente e, com o tempo, tornaria o nosso cronómetro cada vez mais impreciso. Para resolver este problema, cada iteração de laço deve calcular o tempo preciso para dormir, que normalmente é ligeiramente inferior a um segundo (para temporizadores de intervalo de um segundo). A ferramenta sleepenh faz isto por si.
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 se quiser um sinal quando atinge o zero, pode-se, por exemplo, construí-lo com um comando que devolva um estado de saída não zero a zero e combiná-lo com watch -b
, ou algo assim, mas se se quiser construir um guião mais elaborado, este provavelmente não é o caminho a seguir; é mais uma solução do tipo “rápida e suja de uma linha”.
Eu gosto do programa watch
em geral. Vi-o pela primeira vez depois de já ter escrito incontáveis loops while sleep 5; do
para efeitos diferentes. watch
foi demonstravelmente mais agradável.
O sw é um simples cronómetro que irá funcionar para sempre.
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
0x1&
Finja que é uma pessoa no OSX à procura de um cronómetro de linha de comando. Finja que não quer instalar as ferramentas gnu e apenas quer correr com o unix date
nesse caso faça como diz @terdon, mas com esta modificação:
function stopwatch(){
date1=`date +%s`;
while true; do
echo -ne "$(date -jf "%s" $((`date +%s` - $date1)) +%H:%M:%S)\r";
sleep 0.1
done
}
Basta usar relógio + data na hora UTC. Também se pode instalar algum pacote para grande visualização…
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'
Experimente!
Veja também http://www.cyberciti.biz/faq/create-large-colorful-text-banner-on-screen/
Um exemplo 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()
Uma versão GUI do cronómetro
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
Encontrei esta pergunta hoje cedo, quando procurei uma aplicação de termo para exibir um grande contador decrescente para uma oficina. Nenhuma das sugestões era exactamente o que eu precisava, por isso juntei rapidamente outra em Go: https://github.com/bnaucler/cdown
Como a pergunta já está suficientemente respondida, considere que isto é para bem da posteridade.
$ 1500 && xterm -fg amarelo -g 240x80 &
Quando aquele grande terminal com texto amarelo salta, está na hora de se levantar e esticar!
Notas: - 1500 segundos = 25 minutos pomodoro - 240x80 = tamanho do terminal com linha de 240 caracteres, e 80 filas. Preenche um ecrã para mim de forma perceptível.
Crédito: http://www.linuxquestions.org/questions/linux-newbie-8/countdown-timer-for-linux-949463/
Isto é semelhante à resposta aceite, mas terdon’s countdown()
gave me erros de sintaxe. Este funciona bem para mim, no entanto:
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
Pode colocá-lo em timer t
e depois executá-lo com: 0x6&& (onde t é tempo em minutos).
Se quiser um programa compilável por qualquer razão, o seguinte funcionaria:
#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 << "0x1&33[2A0x1&33[K";
std::this_thread::sleep_for(milliseconds(100));
}
std::cout << "Finished" << std::endl;
return 0;
}
Isto pode ser utilizado noutros programas também e facilmente portado, se um ambiente de bash não estiver disponível ou se apenas preferir utilizar um programa compilado github