Como somar o tempo usando o bash?


12

Quero saber a quantidade total de tempo que uma série de processos levaria no meu computador para decidir se eu deveria rodar lá ou em um computador mais forte. 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 percorrer cada linha

awk '{sum += $2 } END { print sum }

mas isso não faz sentido, pois os valores não são números naturais.

Respostas:


15
#!/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" +%sdá 0. Então, date -u -d "jan 1 1970 00:03:34" +%sdá 214 segundos.


Parece um pouco hack-y, mas, ei, funciona de uma maneira interessante (mais ou menos).
Hjk

4

Supondo que você esteja usando o comando interno bash 'time', antes de executar seu programa, você pode export TIMEFORMAT=%0R. A saída será então adicionada em segundos inteiros pelo awk. Mais informações estão disponíveis na seção 'Shell Variables' da página de manual do bash.


4

Se você não pode (ou não quer) usar TIMEFORMATapenas o tempo necessário para transferir o tempo em segundos, adicione-o. Por exemplo, saída de tubulação 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 o último echocomando por

printf "%02d:%02d:%02d\n" $[sum/3600] $[sum/60] $[sum%60]

Os minutos devem ser $[sum/60%60]para tirar as horas.
Jesse Chisholm

3

Atualizar:

Aqui está uma nova implementação que faz uso da dc"base de saída" de s. Observe que se a soma total for superior a 60 horas, isso produzirá quatro valores separados por espaço em vez de três. (E se a soma total for inferior a uma hora, apenas dois valores separados por espaço serão exibidos.)

awk '{print $2}' file.txt | tr : \ | dc -f - -e '60o0ddd[+r60*+r60d**+z1<a]dsaxp'

A entrada é assumida em triplos de hora, minuto, segundo, conforme mostrado na pergunta.

A saída na entrada fornecida é:

 16 43

Resposta original:

Vamos fazer isso com dcsua calculadora de mesa. É o back-end bce é extremamente flexível, embora muitas vezes considerado enigmático.


Primeiro, algum pré-processamento para fornecer apenas os horários e converter os dois pontos em espaços:

awk '{print $2}' | tr : ' '

Também poderíamos fazer isso com o Sed:

sed -En -e 's/^.*([0-9][0-9]):([0-9][0-9]):([0-9][0-9]).*$/\1 \2 \3/p'

Eu vou com o Awk e trporque é mais simples. Qualquer um dos comandos acima produz uma saída limpa no seguinte formato. (Estou usando meu próprio texto de exemplo porque o considero mais interessante; ele inclui horas. O seu também funcionará.)

$ cat input
9 39 42
8 04 50
7 49 32
10 01 54
7 19 18

Dado os tempos no formato acima, execute-os através do seguinte script Sed e canalize o resultado dcconforme mostrado:

sed -e '1s/^/0 /' -e 's/$/ r 60 * + r 60 60 * * + +/' -e '$s/$/ 60 60 * ~ 60 ~ f/' input | dc

(Dividido 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. (Observe que esta é uma sequência invertida.) Estou apenas aprendendo, dcportanto, essa não é uma solução perfeita, mas acho que é muito bom para uma primeira olhada 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
$ 

2

Aqui está a minha solução - use split(). Tempo total de impressão em segundos:

awk '{
        split($2, tm, ":");
        tottm += tm[3] + tm[2] * 60 + tm[1] * 3600;
    }
    END {
        print tottm;
    }' ptime

Imprimindo no formato de hora agradável:

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; portanto, os resultados seriam confusos.


Você pode chamar gawkcom TZ=UTC0 gawk...a remover o problema de fuso horário.
Stéphane Chazelas

2

Basta dividir e calcular:

awk -F '[ :]' '
  {sum += $NF + 60 * ($(NF-1) + 60 * $(NF-2))}
  END {print sum}'

0

Variação de temas aqui, mas mais tubos

echo """
process1    00:03:34
process2    00:00:35
process3    00:12:34
"""  | \
     sed 's/process.  *\([0-9]\)/\1/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

0

Quase qualquer shell poderia 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 $=tno zsh para dividir.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.