как измерить время выполнения программы и сохранить его в переменной

<p>чтобы выяснить, сколько времени занимают определенные операции в скрипте bash (v4 +), я бы хотел проанализировать вывод команды <code>time</code> "по отдельности" и (в конечном итоге) перехватить его в переменной bash (<code>let varname=...</code>).</p><p>теперь я использую <code>time -f '%e' ...</code> (или скорее <code>command time -f '%e' ...</code> из-за встроенного bash), но так как я уже перенаправил вывод выполненной команды, я действительно заблудился относительно того, как я собираюсь захватить выходные данные команды <code>time</code>. в основном, проблема здесь в <em>для отделения вывода</em> of <code>time</code> от вывода выполненной команды.</p><p>то, что я хочу, это <em>функциональность</em> подсчета времени в секундах (целых) между началом команды и ее завершением. это не обязательно должна быть команда <code>time</code> или соответствующая встроенная команда.</p><hr><p><em>edit:</em> учитывая два полезных ответа ниже, я хотел бы добавить два разъяснения.</p><ol> <li>я не хочу отбрасывать вывод выполненной команды, но на самом деле не имеет значения, окажется ли он на stdout или stderr.</li> <li>я бы предпочел прямой подход, а не косвенный (т. е. перехватывать вывод напрямую, а не сохранять его в промежуточных файлах).</li> </ol><p>решение, использующее <code>date</code>, до сих пор подходит к тому, что я хочу.</p>

<p>чтобы получить вывод <code>time</code> в переменную, используйте следующее:</p><pre><code>usr@srv $ mytime="$(time ( ls ) 2>&1 1>/dev/null )" usr@srv $ echo "$mytime" real 0m0.006s user 0m0.001s sys 0m0.005s </code></pre><p>вы также можете просто запросить один тип времени, например, utime:</p><pre><code>usr@srv $ utime="$( timeformat='%lu';time ( ls ) 2>&1 1>/dev/null )" usr@srv $ echo "$utime" 0m0.000s </code></pre><p>чтобы получить время, вы также можете использовать <code>date +%s.%n</code>, поэтому возьмите его до и после выполнения и рассчитайте разницу:</p><pre><code>start=$(date +%s.%n) command end=$(date +%s.%n) diff=$(echo "$end - $start" | bc) # echo $diff </code></pre>

<p>в bash выходные данные конструкции <code>time</code> возвращаются к стандартной ошибке, и вы можете перенаправить стандартную ошибку конвейера, на которую она влияет. итак, давайте начнем с команды, которая записывает в свой вывод и сообщения об ошибках: <code>sh -c 'echo out; echo 1>&2 err'</code>. чтобы не перепутать поток ошибок команды с выводом из <code>time</code>, мы можем временно перенаправить поток ошибок команды в другой файловый дескриптор:</p><pre><code>{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } </code></pre><p>это записывает <code>out</code> в fd 1, <code>err</code> в fd 3 и время в fd 2:</p><pre><code>{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } \ 3> >(sed 's/^/err:/') 2> >(sed 's/^/time:/') > >(sed 's/^/out:/') </code></pre><p>было бы более приятно иметь <code>err</code> на fd 2 и время на fd 3, поэтому мы поменяем их местами, что обременительно, потому что нет прямого способа поменять местами два дескриптора файла:</p><pre><code>{ { { time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } 3>&2 2>&4; } 4>&3; } 3> >(sed 's/^/time:/') 2> >(sed 's/^/err:/') > >(sed 's/^/out:/') </code></pre><p>это показывает, как вы можете постобработать вывод команды, но если вы хотите захватить как вывод команды, так и ее время, вам нужно работать усерднее. использование временного файла является одним из решений. фактически, это единственное надежное решение, если вам нужно зафиксировать как стандартную ошибку команды, так и ее стандартный вывод. но в противном случае вы можете захватить весь вывод и воспользоваться тем, что <code>time</code> имеет предсказуемый формат (если вы используете <code>time -p</code> чтобы получить <a href="http://pubs.opengroup.org/onlinepubs/009695399/utilities/time.html#tag\_04\_141\_11">формат posix</a> или переменную <code>timeformat</code> для bash).</p><pre><code>nl=$'n' output=$(timeformat='%r %u %s %p'; mycommand) set ${output##$nl}; real\_time=$1 user_time=$2 system\_time=$3 cpu_percent=$4 output=${output%$nl*} </code></pre><p>если вы заботитесь только о времени настенных часов, выполнение <code>date</code> до и после является простым решением (если немного более неточным из-за дополнительного времени, потраченного на загрузку внешней команды).</p>

<p>если вы находитесь в <code>bash</code> (а не в <code>sh</code>) и вам не нужна точность менее секунды, вы можете полностью пропустить вызов <code>date</code> и сделать это, не вызывая дополнительных процессы, без необходимости отделять объединенный вывод, и без необходимости собирать и анализировать вывод из каких-либо команд:</p><pre><code># seconds is a bash special variable that returns the seconds since set. seconds=0 mycmd <infile >outfile 2>errfile duration\_in\_seconds=$seconds # now $duration\_in\_seconds` is the number of seconds. </code></pre>

<p>со временем выходные данные команды выводятся на стандартный вывод, а время - на стандартный вывод. итак, чтобы разделить их, вы можете сделать:</p><pre><code>command time -f '%e' [command] 1>[command output file] 2>[time output file] </code></pre><p>но сейчас время в файле. я не думаю, что bash может напрямую помещать stderr в переменную. если вы не против перенаправить вывод команды куда-то, вы можете сделать:</p><pre><code>foo=$((( command time -f '%e' [command]; ) 1>outputfile; ) 2>&1; ) когда вы сделаете это, выходные данные команды будут в outputfile, а время, необходимое для выполнения, будет в $foo</code>.</p>

<p>во все предыдущие ответы, когда в osx</p><pre><code>productname: mac os x productversion: 10.11.6 buildversion: 15g31+ </code></pre><p>ты можешь делать как</p><pre><code>microtime() { python -c 'import time; print time.time()' } compute() { local start=$(microtime) #$1 is command $2 are args local end=$(microtime) diff=$(echo "$end - $start" | bc) echo "$1\t$2t$diff" } </code></pre>

<p>для этой цели, вероятно, лучше использовать <code>times</code> (чем <code>time</code>) в <code>bash</code>, который печатает накопленное пользовательское и системное время для командной консоли и для процессов, запускаемых из командной консоли, например использовать:</p><pre><code>$ (sleep 2; times) | (read tuser tsys; echo $tuser:$tsys) 0m0.001s:0m0.003s видеть: help -m times для получения дополнительной информации.

установить /bin/time (например, pacman -s time)так что вместо ошибки при попытке -f флаг:$ time -f %e sleep 0.5 bash: -f: command not found real 0m0.001s user 0m0.001s sys 0m0.001s </code></pre><p>вы можете фактически использовать это:</p><pre><code>$ /bin/time -f %e sleep 0.5 0.50 и получите то, что вы хотите - время в переменной (пример использует %e для реального истекшего времени, для других параметров проверьте man time):#!/bin/bash tmpf="$(mktemp)" /bin/time -f %e -o "$tmpf" sleep 0.5 variable="$(cat "$tmpf")" rm "$tmpf" echo "$variable" 

так же, как хедз-ап, работая с приведенными выше утверждениями и особенно принимая во внимание ответ гжегожа. я был удивлен, увидев на моей системе ubuntu 16.04 xenial эти два результата:$ which time /usr/bin/time $ time -f %e sleep 4 -f: command not found real 0m0.071s user 0m0.056s sys 0m0.012s $ /usr/bin/time -f %e sleep 4 4.00 </code></pre><p>у меня не установлены псевдонимы, и поэтому я не знаю, почему это происходит.</p>

<p>попробуйте это, он запускает простую команду с аргументами и помещает времена $ real $ user $ sys и сохраняет код выхода.он также не разветвляет подоболочки и не вмешивается ни в какие переменные, кроме реального пользователя sys, и не мешает выполнению сценария.timer () { { time { "$@" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$bashpid.${#funcname[@]}" set -- $? read -d "" \_ real \_ user \_ sys \_ < "/tmp/$$.$bashpid.${#funcname[@]}" rm -f "/tmp/$$.$bashpid.${#funcname[@]}" return $1 } </code></pre><p>например.</p><pre><code> timer find /bin /sbin /usr rm /tmp/ echo $real $user $sys примечание: это всего лишь простая команда, а не конвейер, компоненты которого запускаются в подоболочкеэта версия позволяет указать в качестве $ 1 имя переменных, которые должны получить 3 раза:</p><pre><code>timer () { { time { "${@:4}" ; } 2>${\_} {\_}>&- ; } {\_}>&2 2>"/tmp/$$.$bashpid.${#funcname[@]}" set -- $? "$@" read -d "" _ "$2" \_ "$3" _ "$4" \_ < "/tmp/$$.$bashpid.${#funcname[@]}" rm -f "/tmp/$$.$bashpid.${#funcname[@]}" return $1 } </code></pre><p>например.</p><pre><code> timer r u s find /bin /sbin /usr rm /tmp/ echo $r $u $s </code></pre><p>и может быть полезно, если в конечном итоге он вызывается рекурсивно, чтобы не растоптать время; но тогда они должны быть объявлены локальными при их использовании.</p><p> <a href="http://blog.sam.liddicott.com/2016/01/timeing-bash-commands.html">http://blog.sam.liddicott.com/2016/01/timeing-bash-commands.html</a> </p>