#!/bin/sh
EPOCH='jan 1 1970'
sum=0
for i in 00:03:34 00:00:35 00:12:34
do
sum="$(date -u -d "$EPOCH $i" +%s) + $sum"
done
echo $sum|bc
date -u -d "jan 1 1970" +%s
dá 0. Então date -u -d "jan 1 1970 00:03:34" +%s
dá 214 segundos.
Eu quero saber a quantidade total de tempo que uma série de processos levaria em meu computador para decidir se eu deveria estar em um computador mais strong. Então, estou prevendo o tempo de execução de cada comando. A saída se parece com:
process1 00:03:34
process2 00:00:35
process3 00:12:34
Como posso somar a segunda coluna para obter um tempo total de execução? Eu poderia tentar picar cada linha através de
awk '{sum += $2 } END { print sum }
mas isso não faz sentido, pois os valores não são naturais.
#!/bin/sh
EPOCH='jan 1 1970'
sum=0
for i in 00:03:34 00:00:35 00:12:34
do
sum="$(date -u -d "$EPOCH $i" +%s) + $sum"
done
echo $sum|bc
date -u -d "jan 1 1970" +%s
dá 0. Então date -u -d "jan 1 1970 00:03:34" +%s
dá 214 segundos.
Supondo que você esteja usando o comando embutido bash 'time', antes de executar seu programa, você pode export TIMEFORMAT=%0R
. A saída será então em segundos inteiros addable por awk. Mais informações estão disponíveis na seção "Variáveis Shell" da página man bash.
Se você não puder (ou não quiser) usar TIMEFORMAT
, basta transferir o tempo em segundos e adicioná-lo. Por exemplo, saída de pipe através de:
{
sum=0
while IFS="[ :]" proc_name h m s
do
let sum+=$((60*($m+60*$h)+$s))
done
echo $sum
}
Ou se você quiser, pode trocar a última echo
-command por
printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]
Aqui está a minha solução - use split()
.
Imprimindo o tempo total em segundos:
awk '{
split($2, tm, ":");
tottm += tm[3] + tm[2] * 60 + tm[1] * 3600;
}
END {
print tottm;
}' ptime
Imprimir em bom formato:
awk '{
split($2, tm, ":");
secs += tm[3];
mins += tm[2] + int(secs / 60);
hrs += tm[1] + int(mins / 60);
secs %= 60; mins %= 60;
}
END {
printf "%d:%d:%d\n", hrs, mins, secs;
}' ptime
O GNU awk também suporta strftime, mas usará seu fuso horário atual, então os resultados serão confusos.
Apenas divida e calcule:
awk -F '[ :]' '
{sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
END {print sum}'
Atualização:
Aqui está uma nova implementação que faz uso da "base de saída" de dc
. Observe que, se a soma total for maior que 60 horas, isso gerará quatro valores separados por espaço, em vez de três. (E se a soma total for menor que uma hora, apenas dois valores separados por espaço serão gerados.)
awk '{print $2}' file.txt | tr : \ | dc -f - -e '60o0ddd[+r60*+r60d**+z1<a]dsaxp'
Supõe-se que a entrada seja em triplas de hora, minuto, segundo, conforme mostrado na pergunta.
A saída na entrada fornecida é:
16 43
Resposta original:
Vamos fazer isso com dc
da sua calculadora de mesa. É o back-end para bc
e é extremamente flexível, embora muitas vezes considerado enigmático.
Primeiro, algum pré-processamento para dar apenas os tempos e converter os dois pontos em espaços:
awk '{print $2}' | tr : ' '
Também podemos fazer isso com Sed:
sed -En -e 's/^.*([0-9][0-9]):([0-9][0-9]):([0-9][0-9]).*$/ /p'
Eu vou com Awk e tr
porque é mais simples. Qualquer um dos comandos acima produz uma saída limpa no seguinte formato. (Eu estou usando meu próprio texto de exemplo porque eu considero isso mais interessante; ele tem horas incluídas. O seu também funcionará.)
$ cat input
9 39 42
8 04 50
7 49 32
10 01 54
7 19 18
Em determinados momentos no formato acima, execute-os através do seguinte script Sed e canalize o resultado em dc
como mostrado:
sed -e '1s/^/0 /' -e 's/$/ r 60 * + r 60 60 * * + +/' -e '$s/$/ 60 60 * ~ 60 ~ f/' input | dc
(quebrado para reduzir a rolagem lateral):
sed <input \
-e '1s/^/0 /' \
-e 's/$/ r 60 * + r 60 60 * * + +/' \
-e '$s/$/ 60 60 * ~ 60 ~ f/' |
dc
A saída será segundos, minutos, horas, nessa sequência. (Note que esta é uma sequência invertida). Estou apenas aprendendo dc
, então esta não é uma solução perfeita, mas eu acho que é muito bom para uma primeira olhada em dc
.
Exemplo de entrada e saída, colado diretamente do meu terminal:
$ cat input
9 39 42
8 04 50
7 49 32
10 01 54
7 19 18
$ sed -e '1s/^/0 /' -e 's/$/ r 60 * + r 60 60 * * + +/' -e '$s/$/ 60 60 * ~ 60 ~ f/' input | dc
16
55
42
$
Variação de temas aqui, mas mais cachimbos
echo """
process1 00:03:34
process2 00:00:35
process3 00:12:34
""" | \
sed 's/process. *\([0-9]\)//g' | \
xargs -i{} date +%s --date "1970-1-1 {}" | \
awk '{sum += $1; cnt +=1}
END {
print sum / 60, " total minutes processing";
print (sum / 3600) / cnt, "minutes per job"
}'
916.717 total minutes processing
5.09287 minutes per job
Quase qualquer casca pode fazer as contas:
#!/bin/sh
IFS=:; set +f
getseconds(){ echo "$(( ($1*60+$2)*60+$3 ))"; }
for t in 00:03:34 00:00:35 00:12:34
do sum=$((sum + $(getseconds $t) ))
done
printf '%s\n' "$sum"
Use getseconds $=t
em zsh para dividir.
Tags bash time arithmetic