Как суммировать время, используя bash?


12

Я хочу знать общее количество времени, которое займет ряд процессов на моем компьютере, чтобы решить, должен ли я работать там или на более сильном компьютере. Итак, я прогнозирую время выполнения каждой команды. Вывод выглядит так:

process1    00:03:34
process2    00:00:35
process3    00:12:34

Как суммировать второй столбец, чтобы получить общее время выполнения? Я мог бы попробовать пропустить каждую строку через

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

но это не имеет смысла, так как значения не являются натуральными числами.

Ответы:


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" +%sдает 0. Так date -u -d "jan 1 1970 00:03:34" +%sдает 214 сек.


Кажется немного хакерским, но эй, это работает интересным (вроде) способом.
HJK

4

Предполагая, что вы используете встроенную команду bash 'time', перед запуском вашей программы вы можете это сделать export TIMEFORMAT=%0R. Выходные данные затем будут добавлены в awk за целые секунды. Более подробная информация доступна в разделе «Переменные оболочки» на странице руководства bash.


4

Если вы не можете (или не хотите) использовать TIMEFORMATпросто перенесенное время в секунды, то сложите его вместе. Например, труба выводится через:

{                                           
sum=0
while IFS="[ :]" proc_name h m s
do
    let sum+=$((60*($m+60*$h)+$s)) 
done 
echo $sum  
} 

Или, если хотите, можете обменять последнюю echoкоманду

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

Минуты должны быть $[sum/60%60]лишены часов.
Джесси Чисхолм

3

Обновить:

Вот новая реализация, которая использует dc«выходную базу». Обратите внимание, что если общая сумма составляет более 60 часов, это приведет к выводу четырех разделенных пробелами значений вместо трех. (И если общая сумма меньше одного часа, будут выведены только два значения через пробел.)

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

Предполагается, что ввод вводится в тройках часов, минут, секунд, как показано в вопросе.

Выход на указанный вход:

 16 43

Оригинальный ответ:

Давайте сделаем это с dcвашим настольным калькулятором. Это серверная часть bc, и она чрезвычайно гибкая, хотя часто считается загадочной.


Во-первых, некоторые предварительные обработки, чтобы дать только времена, и преобразовать двоеточия в пробелы

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

Мы могли бы также сделать это с Sed:

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

Я пойду с Awk и trпотому что это проще. Любая из приведенных выше команд производит чистый вывод в следующем формате. (Я использую свой собственный пример текста, потому что считаю его более интересным; в него включены часы. Ваш также будет работать.)

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

Для заданного времени в указанном выше формате запустите их с помощью следующего сценария Sed и передайте результат в dcвиде, показанном ниже:

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

(Сломан, чтобы уменьшить боковую прокрутку :)

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

Выходными данными будут секунды, минуты, часы в этой последовательности. (Обратите внимание, что это обратная последовательность.) Я только учусь, dcтак что это не идеальное решение, но я думаю, что это довольно хорошо для первого взгляда dc.

Пример ввода и вывода, вставленный прямо из моего терминала:

$ 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

Вот мое решение - использовать split(). Общее время печати в секундах:

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

Печать в удобном формате времени:

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

GNU awk также поддерживает strftime, но будет использовать ваш текущий часовой пояс, поэтому результаты могут привести к путанице.


Вы можете позвонить gawkс , TZ=UTC0 gawk...чтобы снять вопрос часового пояса.
Стефан Шазелас


0

Вариация на темы здесь, но больше труб

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

Почти любая оболочка может сделать математику:

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

Используйте getseconds $=tв zsh, чтобы разделить его.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.