I would like to display the completion time of a script.
What I currently do is –
#!/bin/bash date ## echo the date at start # the script contents date ## echo the date at end
This just show’s the time of start and end of the script. Would it be possible to display a fine grained output like processor time/ io time , etc?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Just use time when you call the script:
time yourscript.sh
Method 2
If time isn’t an option,
start=`date +%s` stuff end=`date +%s` runtime=$((end-start))
or, if you need sub-second precision and have bc installed,
start=`date +%s.%N` stuff end=`date +%s.%N` runtime=$( echo "$end - $start" | bc -l )
Method 3
Just call times without arguments upon exiting your script.
With ksh or zsh, you can also use time instead. With zsh, time will also give you the wall clock time in addition to the user and system CPU time.
To preserve the exit status of your script, you can make it:
ret=$?; times; exit "$ret"
Or you can also add a trap on EXIT:
trap times EXIT
That way, times will be called whenever the shell exits and the exit status will be preserved.
$ 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
Also note that all of bash, ksh and zsh have a $SECONDS special variable that automatically gets incremented every second. In both zsh and ksh93, that variable can also be made floating point (with typeset -F SECONDS) to get more precision. This is only wall clock time, not CPU time.
Method 4
I’m a bit late to the bandwagon, but wanted to post my solution (for sub-second precision) in case others happen to stumble upon this thread through searching. The output is in format of days, hours, minutes, and finally seconds:
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) LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%02.4fn" $dd $dh $dm $ds
Hope someone out there finds this useful!
[edit] You need to count all characters in field definition in bash printf, if you want pad seconds to 2 digits before dot you have to define it as %07.4f (all digits and dot count too in to filed length) so the line should look like:
LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%07.4fn" $dd $dh $dm $ds
Method 5
My method for bash:
# Reset BASH time counter
SECONDS=0
#
# do stuff
#
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
Method 6
Personally, I like to wrap all my script code in some “main” function like so:
main () {
echo running ...
}
# stuff ...
# calling the function at the very end of the script
time main
Notice how easy is to use the time command in this scenario. Obviously you’re not measuring the precise time including script parse time, but I find it accurate enough in most situations.
Method 7
This question is quite old but in trying to find my favorite way of doing it this thread came up high… and I’m surprised no one mentioned it:
perf stat -r 10 -B sleep 1
‘perf’ is a performance analyzing tool included in the kernel under ‘tools/perf’ and often available to install as a separate package (‘perf’ in CentOS and ‘linux-tools’ on Debian/Ubuntu). The Linux Kernal perf Wiki has much more information about it.
Running ‘perf stat’ gives quite a bit of details including average execution time right at the end:
1.002248382 seconds time elapsed ( +- 0.01% )
Method 8
#!/bin/bash
start=$(date +%s.%N)
# HERE BE CODE
end=$(date +%s.%N)
runtime=$(python -c "print(${end} - ${start})")
echo "Runtime was $runtime"
Yes, this calls Python, but if you can live with that then this is quite a nice, terse solution.
Method 9
A small shell function that can be added before commands to measure their time:
tm() {
local start=$(date +%s)
<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e1c5a1">[email protected]</a>
local exit_code=$?
echo >&2 "took ~$(($(date +%s)-${start})) seconds. exited with ${exit_code}"
return $exit_code
}
Then use it in your script, or on your command line like so:
tm the_original_command with all its parameters
Method 10
#!/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 <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a7d4cfcecfc4cfc2c9c089c0d2c8e7c0cac6cecb89c4c8ca">[email protected]</a>
#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..."
Method 11
-
Just use
time [any command]. Ex:time sleep 1will sleep for a real time (ie: as timed by a stop watch) of ~1.000 to ~1.020 sec, as shown here:$ time sleep 1 real 0m1.011s user 0m0.004s sys 0m0.000sWhat a beautiful thing. You can put any command after it, and it outputs the result in a nice, human-readable form. I really like to use it for timing builds. Ex:
# time your "make" build time make # time your "Bazel" build time bazel build //path/to/some:target…or for git operations which can potentially be really long, so I can develop realistic mental expectations:
# 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 -
For more-customized timing needs where you may need to manipulate the output or convert it to other forms, in bash, use the internal
$SECONDSvariable. Here’s a demo, including converting these seconds to other units, such as floating point minutes:Note that
dt_mingets rounded from0.01666666666...(1 second = that many minutes) to0.017in this case since I’m using theprintffunction to round. Thesleep 1;part below is where you’d call your script to run and time, but I’m just sleeping for 1 second instead for the sake of this demo.Command:
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"Output:
dt_sec = 1; dt_min = 0.017
Related:
- Read more about
bcandprintfin my answer here: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867 - I don’t remember where I first learned about the
timecommand anymore, but it may have been from @Trudbert’s answer right here.
Method 12
The accepted solution using time writes to stderr.
The solution using times writes to stdout.
The solutions using $SECONDS are missing sub-second precision.
The other solutions involve calling external programs like date or perf which is not efficient.
If you are fine with any of these, use it.
But if you need an efficient solution to get the times with millisecond precision and need them into variables such that the original output remains undisturbed you may combine process substitution with some redirections around time which is much faster than calling external programs and allows redirections around the timing wrapper script as on the original command/script.
# 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
Select one of the following variants parsing the output of time appropriate to you needs:
Shortest variant where stdout of $Cmd is written to stderr and nothing to stdout:
read Elapsed User System < <({ time $Cmd 2>&3; } 3>&2 2>&1 >&3)
Longer variant that keeps original stdout and stderr separate of each other:
{ read Elapsed User System < <({ time $Cmd 2>&4; } 4>&2 2>&1 >&3); } 3>&1
Most complicated variant includes closing the extra file descriptors such that $Cmd is called as if without this timing wrapper around it and lvm commands like vgs do not complain about leaked file descriptors:
{ read Elapsed User System < <({ time $Cmd 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1
You can even fake a floating point addition in bash without calling bc which would be much slower:
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
Possible outputs with the two examples of $Cmd on a slow CPU:
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
Or:
stdout stderr CPU 0.008 s, Elapsed 0.109 s
Method 13
Using only bash it is also possible to measure and calculate the time duration for a portion of the shell script (or the elapsed time for the entire script):
start=$SECONDS ... # do time consuming stuff end=$SECONDS
you can now either just print the difference:
echo "duration: $((end-start)) seconds."
if you only need incremental duration do:
echo "duration: $((SECONDS-start)) seconds elapsed.."
You can also store the duration in a variable:
let diff=end-start
Method 14
Timing function based on SECONDS, limited to second-level granularity only, doesn’t use any external commands:
time_it() {
local start=$SECONDS ts ec
printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
printf '%sn' "$ts Starting $*"
"<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="042044">[email protected]</a>"; ec=$?
printf -v ts '%(%Y-%m-%d_%H:%M:%S)T' -1
printf '%sn' "$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"
}
For example:
time_it sleep 5
gives
2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished sleep 5; elapsed = 5 seconds
Method 15
Use bash time builtin?
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.
Example:
TIMEFORMAT="The command took %Rs"
time {
sleep 0.1
}
Output:
The command took 0.108s
Method 16
Here’s a variation of Alex’s answer. I only care about minutes and seconds, but I also wanted it formatted differently. So I did this:
start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
Method 17
#!/bin/bash begin=$(date +"%s") Script termin=$(date +"%s") difftimelps=$(($termin-$begin)) echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."
Method 18
Another buildin solution (according to: https://stackoverflow.com/a/385422/1350091) is:
/usr/bin/time -v command
Method 19
similar to @LeZuse’s response but supports script arguments:
#!/bin/bash
main () {
echo "my first argument is $1"
# script content here
}
# "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3e1a7e">[email protected]</a>" will expand to the script's arguments
time main "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3c187c">[email protected]</a>"
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0