Як ефективно отримати час виконання сценарію?


339

Я хотів би показати час завершення сценарію.

Я зараз роблю -

#!/bin/bash
date  ## echo the date at start
# the script contents
date  ## echo the date at end

Це просто показує час початку та закінчення сценарію. Чи можна було б відобразити дрібнозернистий вихід, наприклад, час процесора / іо та інше?



Я думаю, що прийнятої відповіді на сьогоднішній день насправді недостатньо через часові коливання.
Лео Леопольд Герц 준영

1
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Я вважаю, що запропонована тема все ще недостатня для усунення ефекту тимчасових подій.
Лео Леопольд Герц 준영

Крім того, пов'язані з : stackoverflow.com/questions/5152858 / ... або кілька unix.stackexchange.com/questions/334145 / ...
PHK

Існує команда, яку можна запустити, а потім вона автоматично відтворює всі команди Unix, забувши, як увімкнути?
порошок366

Відповіді:


448

Просто використовуйте timeпід час виклику сценарію:

time yourscript.sh

61
Це виводить три рази: real, userі sys. Про значення цих питань дивіться тут .
Гарретт

27
"справжній" - це, мабуть, те, що люди хочуть знати - "Реальний - це годинник настінних годин - час від початку до закінчення дзвінка"
Бред Паркс

3
Чи є спосіб зафіксувати stdout у файл? Наприклад, щось на кшталтtime $( ... ) >> out.txt
Джон,

1
Ви також можете це зробити для послідовностей команд у командному рядку, наприклад:time (command1 && command2)
meetar

1
Або він міг би за своїм сценарієм просто зробити: echo $ SECONDS припускаючи, що це удар ....
Crossfit_and_Beer

169

Якщо timeце не варіант,

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

runtime=$((end-start))

30
Зауважте, що це працює лише в тому випадку, якщо вам не потрібна точність другої секунди. Для деяких застосувань це може бути прийнятним, для інших - не. Для дещо кращої точності (наприклад, ви все ще викликаєте dateдвічі, наприклад, ви можете в кращому випадку отримати мілісекундну точність на практиці, і, мабуть, менше), спробуйте використовувати date +%s.%N. ( %Nє наносекундами з усієї секунди.)
CVn

Влучне зауваження. Я подумав про це відразу після виходу з клавіатури, але не повернувся. ^^ Також пам’ятайте, OP, що «дата» сама додасть часу виконання декількох мілісекунд.
Роб Бос

2
@ChrisH О. Гарно вказуючи це; арифметичне розширення bash є лише цілим числом. Я бачу два очевидних варіанти; або відкиньте період у рядку формату дати (і трактуйте отримане значення як наносекунд з епохи), тому використовуйте date +%s%Nабо використовуйте щось більш здатне, як bcобчислити фактичний час виконання з двох значень, як пропонує jwchew. Але я вважаю, що цей підхід є неоптимальним способом його виконання; timeзначно кращий за наявності, з причин, викладених вище.
CVn

10
Просто встановіть, bcа потім зробіть це:runtime=$( echo "$end - $start" | bc -l )
повторний

10
Якщо ваш сценарій займає кілька хвилин, використовуйте:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77

75

Просто зателефонуйте 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мають $SECONDSспеціальну змінну, яка автоматично збільшується щосекунди. І в тому, zshі в ksh93цій змінній також можна зробити плаваючу точку (з typeset -F SECONDS) для отримання більшої точності. Це лише час настінного годинника, а не час процесора.


12
Ця змінна $ SECONDS дуже корисна, дякую!
Андібуклі

Чи усуває ваш підхід вплив часових подій? - - Я думаю, що ваш підхід є timeпідходом, представленим раніше. - - Я думаю, що timeitпідхід, представлений у коді MATLAB, може бути тут корисним.
Лео Леопольд Герц 준영

2
Привіт, @ Стефан Шазелас. Я виявив, timesщо не дає часу на стіну, правда? наприклад,bash -c 'trap times EXIT;sleep 2'
user15964


32

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

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

Сподіваюся, хтось там знайде це корисним!


1
Я думаю, немає іншого способу (тільки bash), окрім використання "bc" для розрахунків. BTW дійсно хороший сценарій;)
tvl

1
Будьте уважні, що FreeBSD dateне підтримує точність другої секунди і просто додасть буквальну позначку " N" до часової позначки.
Антон Самсонов

1
Дуже приємний сценарій, але мій баш не обробляє підрядну частину. Я також дізнався, що / 1 в bc ефективно знімає це, тому я додав @ / 1 @ до розрахунку $ ds, і він відображає речі дуже приємно!
Даніель

1
ЬС підтримує по модулю: dt2=$(echo "$dt%86400" | bc). Просто кажу ... До речі, я вважаю за краще форму, dt2=$(bc <<< "${dt}%86400")але це повністю особиста.
Dani_l

16

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

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

Це показує минулий час, але він не показує "дрібнозернистий вихід, як час процесора, час вводу / виводу", як вимагається у запитанні.
roaima

3
Ті, хто шукає відповідь на поставлене запитання, ймовірно, вважають це рішення корисним. Ваш коментар буде краще адресований питанням про ОП.
Марк

5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

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

echo "Runtime was $runtime"

Так, це закликає Python, але якщо ти можеш з цим жити, то це дуже приємне, коротке рішення.


5
Не забудьте, що для запуску цієї pythonкоманди в підклітині та зчитування її результату знадобиться кілька мільйонів наносекунд у більшості поточних систем. Те саме для бігу date.
Стефан Шазелас

7
"Кілька мільйонів наносекунд" - це кілька мілісекунд. Люди, які синхронізують баш сценарії, зазвичай не дуже стурбовані цим (якщо вони не виконують кілька мільярдів сценаріїв на мегасекунд).
Зільк

2
Зауважте також, що навіть якщо обчислення проводиться всередині процесу python, значення були повністю зібрані до виклику python. Час виконання сценарію буде виміряно правильно, без накладних "мільйонів наносекунд".
Віктор Шредер

5

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

perf stat -r 10 -B sleep 1

'perf' - це інструмент аналізу продуктивності, що міститься в ядрі під 'tools / perf' і часто доступний для установки як окремий пакет ('perf' у CentOS та 'linux-tools' на Debian / Ubuntu). Персональна Wiki Wiki Pernal має набагато більше інформації про неї.

Запуск 'perf stat' дає досить багато деталей, включаючи середній час виконання в кінці:

1.002248382 seconds time elapsed                   ( +-  0.01% )

2
Що таке perf?
B Layer

@ B Layer - відредагував мою відповідь, щоб дати короткий опис "perf".
Заахід

1
perf statтепер скаржиться на "Ви можете не мати дозволу збирати статистику". якщо ви не запускаєте деякі команди з sudo, що робить його марним у всіх сценаріях, де ви не повністю володієте цільовою машиною.
аламар

4

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

tm() {
  s=$(date +%s)
  $@
  rc=$?
  echo "took $[$(date +%s)-$s] seconds. return code $rc" >&2
  return $rc
}

Потім використовуйте його у своєму сценарії чи у своєму командному рядку так:

tm the_original_command with all its parameters

Варто було б додати мілісекунди, спробував ilke s=$(date +%s000), не впевнений, чи працює.
loretoparisi

3

Ось варіант відповіді Алекса. Мене хвилюють лише хвилини та секунди, але я також хотів, щоб це було відформатовано інакше. Тому я зробив це:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")

1
Чому потік? У мене клоп?
mpontillo

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

7
Чим це насправді відрізняється від інших вже відповідей, які використовують dateдо та після сценарію та виводять різницю між ними?
Ерік Реноф

2

Використовуючи лише bash, можна також виміряти та обчислити тривалість часу для частини сценарію оболонки (або минулого часу для всього сценарію):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

тепер можна або просто надрукувати різницю:

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

якщо вам потрібна лише додаткова тривалість:

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

Ви також можете зберігати тривалість у змінній:

let diff=end-start

^ ЦЕ відповідь людей. Або простіше: $ SECONDS наприкінці. Це змінна Bash BUILT-IN. Всі інші відповіді просто займаються додатковою роботою, щоб переосмислити це колесо ...
Crossfit_and_Beer

2

Особисто мені подобається вкладати весь свій скриптовий код у якусь "головну" функцію, наприклад:

main () {
 echo running ...
}

# stuff ...

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

Зауважте, як легко використовувати timeкоманду в цьому сценарії. Очевидно, ви не вимірюєте точний час, включаючи час розбору сценарію, але я вважаю це досить точним у більшості ситуацій.


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

Функція синхронізації на основі 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.