Как получить время выполнения сценария эффективно?

awk -F: ' NF!=7 {print}' /etc/passwd

должен сделать это. Если количество полей не 7, то распечатайте его.

363
19.10.2012, 16:26
17 ответов

Просто используйте time когда Вы называете сценарий:

time yourscript.sh
475
27.01.2020, 19:26
  • 1
    Это производит три раза: real, user и sys. Для значений их посмотрите здесь. –  Garrett 09.07.2015, 04:56
  • 2
    и "реальный", вероятно, что люди хотят знать - "Реальный, стена, показывают время - время от начала до конца вызова" –  Brad Parks 29.03.2016, 15:19
  • 3
    Существует ли способ получить stdout в файл? Например, что-то как time $( ... ) >> out.txt –  Jon 25.04.2018, 21:18
  • 4
    Можно также сделать это к последовательностям команд на командной строке, например: time (command1 && command2) –  meetar 25.04.2018, 23:30

Просто звоните times без аргументов после выхода из Вашего сценария.

С ksh или zsh, можно также использовать time вместо этого. С zsh, time также даст Вам, стена показывает время в дополнение к системному процессорному времени и пользователю.

Для сохранения статуса выхода сценария можно сделать его:

ret=$?; times; exit "$ret"

Или можно также включить прерывание EXIT:

trap times EXIT

Тем путем времена назовут каждый раз, когда выходы оболочки и статус выхода будут сохранены.

$ bash -c 'trap times EXIT; : {1..1000000}'
0m0.932s 0m0.028s
0m0.000s 0m0.000s
$ zsh -c 'trap time EXIT; : {1..1000000}'
shell  0.67s user 0.01s system 100% cpu 0.677 total
children  0.00s user 0.00s system 0% cpu 0.677 total

Также обратите внимание что весь из bash, ksh и zsh имейте a $SECONDS специальная переменная, которая автоматически увеличивается каждую секунду. В обоих zsh и ksh93, та переменная может также быть высказана плавающее мнение (с typeset -F SECONDS) получить больше точности. Это - только стена, показывают время, не процессорное время.

77
27.01.2020, 19:26
  • 1
    Та переменная $SECONDS очень полезна, Спасибо! –  andybuckley 18.06.2014, 15:00
  • 2
    Ваш подход устраняет эффект временных событий? - Я думаю, что Ваш подход рядом time подход представлен ранее. - Я думаю timeit подход, представленный в коде MATLAB, может быть полезным здесь. –  Léo Léopold Hertz 준영 25.12.2016, 11:37
  • 3
    Привет, @Stéphane Chazelas. Я нашел times не дает стенное время, правильно? например, bash -c 'trap times EXIT;sleep 2' –  user15964 13.06.2017, 05:17

Если time не опция,

start=`date +%s`
stuff
end=`date +%s`

runtime=$((end-start))
187
27.01.2020, 19:26
  • 1
    Обратите внимание, что это только работает, если Вам не нужна подвторая точность. Для некоторого использования, которое могло бы быть приемлемо для других нет. Для немного лучшей точности (Вы все еще вызываете date дважды, например, таким образом, Вы могли бы в лучшем случае получить точность миллисекунды на практике, и вероятно меньше), попытайтесь использовать date +%s.%N. (%N наносекунды с целой секунды.) –  a CVn 19.10.2012, 22:17
  • 2
    . Я думал об этом сразу после отъезда клавиатуры, но не возвратился. ^^ Также помнят, OP, та "дата" самостоятельно добавит несколько миллисекунд ко времени выполнения. –  Rob Bos 20.10.2012, 18:54
  • 3
    @ChrisH, О. Хорошее указание на него; арифметическое расширение удара только для целого числа. Я вижу две очевидных опции; любая канава период в строке формата даты (и обработка результирующее значение как наносекунды с эпохи), так использование date +%s%N, или используйте что-то более способное как bc вычислить фактическое время выполнения от двух значений как jwchew предлагает. Однако, я чувствую, что этот подход является субоптимальным способом сделать его; time значительно лучше при наличии, по причинам, обрисованным в общих чертах выше. –  a CVn 23.01.2014, 15:29
  • 4
    Просто установка bc и затем сделайте это: runtime=$( echo "$end - $start" | bc -l ) –  redolent 12.02.2015, 00:01
  • 5
    , Если Ваш сценарий занимает несколько Минут, использует: echo "Duration: $((($(date +%s)-$start)/60)) minutes –  rubo77 29.07.2016, 07:42

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

res1=$(date +%s.%N)

# do stuff in here

res2=$(date +%s.%N)
dt=$(echo "$res2 - $res1" | bc)
dd=$(echo "$dt/86400" | bc)
dt2=$(echo "$dt-86400*$dd" | bc)
dh=$(echo "$dt2/3600" | bc)
dt3=$(echo "$dt2-3600*$dh" | bc)
dm=$(echo "$dt3/60" | bc)
ds=$(echo "$dt3-60*$dm" | bc)

printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds

Надеюсь, что кто-то там находит это полезным!

33
27.01.2020, 19:26
  • 1
    , который я предполагаю, нет никого другого (только удар) пути кроме использования 'до н.э', чтобы сделать вычисления. BTW действительно хороший сценарий ;) –  tvl 18.04.2016, 04:19
  • 2
    Остерегайтесь того FreeBSD date не поддерживает подвторую точность и просто добавит литерал “N” к метке времени. А-ч –  Anton Samsonov 12.07.2016, 12:25
  • 3
    Очень хороший сценарий, но мой удар не обрабатывает подвторую часть. Я также учился, это/1 в до н.э эффективно полосах, что, таким образом, я добавил/1 к вычислению $ds, и он отображает материал, очень хороший теперь! прохладный –  Daniel 01.02.2017, 08:00
  • 4
    до н.э поддерживает по модулю: dt2=$(echo "$dt%86400" | bc). Просто высказывание... BTW я предпочитаю форму dt2=$(bc <<< "${dt}%86400") но это является совершенно персональным. –  Dani_l 07.03.2018, 18:06
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

end=$(date +%s.%N)    
runtime=$(python -c "print(${end} - ${start})")

echo "Runtime was $runtime"

Да, это называется Python, но если вы можете жить с этим, то это довольно милое, лаконичное решение.

6
27.01.2020, 19:26
[

] Вот вариант ответа Алекса. Меня волнуют только минуты и секунды, но я также хотел, чтобы он был отформатирован по-другому. Так что я сделал это:[

] [
start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
]
3
27.01.2020, 19:26
#!/bin/bash
begin=$(date +"%s")

Script

termin=$(date +"%s")
difftimelps=$(($termin-$begin))
echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."
1
27.01.2020, 19:26
#!/bin/csh
#PBS -q glean
#PBS -l nodes=1:ppn=1
#PBS -l walltime=10:00:00
#PBS -o a.log
#PBS -e a.err
#PBS -V
#PBS -M shihcheng.guo@gmail.com
#PBS -m abe
#PBS -A k4zhang-group
START=$(date +%s)
for i in {1..1000000}
do
echo 1
done
END=$(date +%s)
DIFF=$(echo "$END - $START" | bc)
echo "It takes DIFF=$DIFF seconds to complete this task..."
3
27.01.2020, 19:26

Мой метод для bash :

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
20
27.01.2020, 19:26

Небольшая функция оболочки, которую можно добавить перед командами для измерения их времени:

tm() {
  local start=$(date +%s)
  $@
  local exit_code=$?
  echo >&2 "took ~$(($(date +%s)-${start})) seconds. exited with ${exit_code}"
  return $exit_code
}

Затем используйте ее в своем сценарии или в командной строке, например:

tm the_original_command with all its parameters
7
27.01.2020, 19:26

Используя только bash, также можно измерять и вычислять продолжительность времени для части сценария оболочки (или прошедшее время для всего сценария):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

теперь вы можете просто распечатать разницу:

echo "duration: $((end-start)) seconds."

если вам нужна только дополнительная продолжительность, выполните:

echo "duration: $((SECONDS-start)) seconds elapsed.."

Вы также можете сохранить продолжительность в переменной:

let diff=end-start
3
27.01.2020, 19:26

Функция синхронизации основана на SECONDS, ограничена только вторым -уровнем детализации, не использует никаких внешних команд:

time_it() {
  local start=$SECONDS ts ec
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Starting $*"
  "$@"; ec=$?
  printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
  printf '%s\n' "$ts Finished $*; elapsed = $((SECONDS-start)) seconds"
  # make sure to return the exit code of the command so that the caller can use it
  return "$ec"
}

Например:

time_it sleep 5

дает

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished
sleep 5; elapsed = 5 seconds
3
27.01.2020, 19:26
  1. Просто используйтеtime [any command]. Пример:time sleep 1будет спать в режиме реального времени (, т.е. :по секундомеру )из ~от 1,000 до ~1,020 с, как показано здесь:

     $ time sleep 1
    
     real    0m1.011s
     user    0m0.004s
     sys 0m0.000s
    

    Какая красота. Вы можете поместить любую команду после нее, и она выведет результат в удобном для человека -читаемом виде. Мне очень нравится использовать его для временных сборок. Экс:

     # time your "make" build
     time make
    
     # time your "Bazel" build
     time bazel build //path/to/some:target
    

    ...или для операций git, которые потенциально могут быть очень длинными,так что я могу развить реалистичные ментальные ожидания:

     # time how long it takes to pull from a massive repo when
     # I'm working from home during COVID-19. NB: `git pull`
     # is sooooo much slower than just pulling the one branch
     # you need with `git pull origin <branch>`, so just fetch
     # or pull what you need!
     time git pull origin master
    
  2. Для более -настраиваемых временных требований , когда вам может потребоваться манипулировать выводом или преобразовывать его в другие формы, в bash используйте внутреннюю $SECONDSпеременную. Вот демонстрация, включающая преобразование этих секунд в другие единицы, такие как минуты с плавающей запятой:

    Обратите внимание, что dt_minокругляется от0.01666666666...(1 секунда = столько минут )до 0.017в данном случае, так как я использую для округления функцию printf. В приведенной ниже части sleep 1;вы бы вызвали свой скрипт для запуска и времени, но вместо этого я просто сплю в течение 1 секунды ради этой демонстрации.

    Команда:

     start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); \
     dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); \
     echo "dt_sec = $dt_sec; dt_min = $dt_min"
    

    Выход:

     dt_sec = 1; dt_min = 0.017
    

Связанные:

  1. Подробнее о bcи printfчитайте в моем ответе здесь:https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Я уже не помню, где я впервые узнал о команде time, но, возможно, это было из ответа @ Trudbert прямо здесь .
5
27.01.2020, 19:26

Лично я предпочитаю заключать весь код своего скрипта в какую-нибудь «главную» функцию, например:

main () {
 echo running...
}

# stuff...

# calling the function at the very end of the script
time main

Обратите внимание, как легко использовать команду timeв этом сценарии. Очевидно, вы не измеряете точное время, включая время анализа скрипта, но я считаю его достаточно точным в большинстве ситуаций.

21
27.01.2020, 19:26

Использовать встроенное время bash?

time: time [-p] PIPELINE
    Execute PIPELINE and print a summary of the real time, user CPU time,
    and system CPU time spent executing PIPELINE when it terminates.
    The return status is the return status of PIPELINE.  The `-p' option
    prints the timing summary in a slightly different format.  This uses
    the value of the TIMEFORMAT variable as the output format.

Пример:

TIMEFORMAT="The command took %Rs"
time {
    sleep 0.1
}

Выход:

The command took 0.108s
3
15.04.2020, 23:13

Принятое решение с использованием timeзаписывается в stderr.
Решение с использованием timesзаписывается в stdout.
В решениях, использующих $SECONDS, отсутствует точность менее -секунды.
Другие решения включают вызов внешних программ, таких как dateили perf, что неэффективно.
Если у вас все в порядке с любым из них, используйте его.

Но если вам нужно эффективное решение для получения времени с миллисекундной точностью и вам нужно преобразовать его в переменные так, чтобы исходный вывод оставался неизменным вы можете комбинировать подстановку процесса с некоторыми перенаправлениями вокруг time, что намного быстрее, чем вызов внешних программ, и позволяет перенаправления вокруг сценария-оболочки синхронизации, как в исходной команде/сценарии.

# Preparations:
Cmd=vgs  # example of a program to be timed which is writing to stdout and stderr
Cmd="eval { echo stdout; echo stderr >&2; sleep 0.1; }"  # other example; replace with your own
TIMEFORMAT="%3R %3U %3S"  # make time output easy to parse

Выберите один из следующих вариантов разбора вывода timeв соответствии с вашими потребностями:
Кратчайший вариант, где stdoutиз $Cmdзаписывается в stderrи ничего вstdout:

read Elapsed User System < <({ time $Cmd 2>&3; } 3>&2 2>&1 >&3)

Более длинный вариант, в котором оригинал stdoutи stderrотделены друг от друга:

{ read Elapsed User System < <({ time $Cmd 2>&4; } 4>&2 2>&1 >&3); } 3>&1

Самый сложный вариант включает в себя закрытие дополнительных файловых дескрипторов, так что $Cmdвызывается как бы без этой временной оболочки вокруг него, а lvmтакие команды, как vgs, не жалуются на утечку файловых дескрипторов:

{ read Elapsed User System < <({ time $Cmd 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1

Вы даже можете имитировать сложение с плавающей запятой в bashбез вызова bc, что было бы намного медленнее:

CPU=`printf %04d $((10#${User/.}+10#${System/.}))`  # replace with your own postprocessing
echo CPU ${CPU::-3}.${CPU: -3} s, Elapsed $Elapsed s >&2  # redirected independent of $Cmd

Возможные результаты двух примеров $Cmdпри медленном ЦП:

File descriptor 3 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash
File descriptor 4 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash
  VG   #PV #LV #SN Attr   VSize VFree
  b3     3  24   0 wz--n- 1.31t 1.19t
CPU 0.052 s, Elapsed 0.056 s

Или:

stdout
stderr
CPU 0.008 s, Elapsed 0.109 s
3
20.08.2020, 01:37

аналогично ответу @LeZuse, но поддерживает аргументы скрипта:

#!/bin/bash

main () {
   echo "my first argument is $1"
   # script content here
}

# "$@" will expand to the script's arguments
time main "$@"
0
07.10.2021, 10:05

Теги

Похожие вопросы