Como somar tempo usando o bash?

8

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.

    
por je_b 05.02.2015 / 20:45

8 respostas

9

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

    
por 06.02.2015 / 15:08
4

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.

    
por 05.02.2015 / 21:07
4

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]
    
por 05.02.2015 / 21:20
2

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.

    
por 05.02.2015 / 21:28
2

Apenas divida e calcule:

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

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
$ 
    
por 03.12.2016 / 06:30
0

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
    
por 15.11.2018 / 21:42
0

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.

    
por 16.11.2018 / 16:43