Just picked up your book Wicked Cool Shell Scripts and already have one quick question: While working on the startup scripts, I would like to place log timers at certain portions of scripts. Is it possible to log point A at he beginning of a section, do the processing, log point B at the end of the section and then log B minus A so I know exactly how long a section took to process?
On first glance, I thought that this would be a particularly tough challenge, with some sort of requirement to parse and translate date and time stamps, but then I realized that it’s actually remarkably straightforward as long as you have a decent version of the date command on your system.
What you want to use is the “+%s” string format to the date command. If you have a good version of the command, it’ll output the number of seconds since the Epoch as a numeric value:
1157490628
Your resolution would be in seconds, but it should be quite easy to run this command just before and just after the block in question, the do a simple subtraction to figure out the elapsed time. Like this:
long block of slow code
after=”$(date +%s)”
elapsed_seconds=”$(expr $after – $before)”
echo Elapsed time for code block: $elapsed_seconds
If you want to go from Epoch time to a more coherent date/time string, you can use the “-r” flag to date. You can figure out the “after” time, for example, this way:
But it turns out that you can also specify a format string to the date command even with the “-r” flag in use, so you could also get the HH:MM:SS value only of the difference between the two values like this:
Hope that helps you see a solution path for your script!
Using -d instead of -r (/bin/date on Linux might be different to Unix variants…)
The following caters for up to 7 days:
#!/usr/bin/env bash
before=$(date +%s)
#do some work
#sleep 35
#after=$(date +%s)
# testing with 6 days and 35 seconds
after=$(( $before + 86400 * 6 + 35 ))
elapsed_seconds=$(( $after – $before ))
[ $elapsed_seconds -gt $(( 86400 * 7 )) ] &[ $elapsed_seconds -gt 86400 ] &” +%u” days, “%H:%M:%S)
} || echo $(date -u -d “@${elapsed_seconds}” +%H:%M:%S)
Above… double ampersand lost after test…. (also formatting error)
Does a code tag work here?
[code]
#!/usr/bin/env bash
before=$(date +%s)
#sleep 35
#after=$(date +%s)
after=$(( $before + 35 + 86400 * 6 ))
elapsed_seconds=$(( $after – $before ))
[ $elapsed_seconds -gt $(( 86400 * 7 )) ] &[ $elapsed_seconds -gt 86400 ] &” +%u” days, “%H:%M:%S)
} || echo $(date -u -d “@${elapsed_seconds}” +%H:%M:%S)
[/code]
I know this post is old but if you’re still reading this I just thought I’d give you a heads up about this:
prompt$> { time { cp source-file folder/ & cp src-file2 folder/ & done & wait; }
The command line above works without a hitch and times the two commands, that are executed in the background, properly.
prompt$> { time { for i in file1 file2; do echo $i folder/ & wait; } }
This one above doesn’t. It exits immediately and gives an inaccurate output of the time command.
Could you shed some light into this please.
Thanks Dave – exactly what I needed!
Very Nice!
exactly what I was looking for.
thanks.
On some systems the folowing command, will give unwanted numbers in the hour column (below)
$ echo $(date -r 110 +%H:%M:%S)
19:01:50
This is because you’re reporting elapsed time since the epoch, but if you’re not on GMT, then the epoch won’t start at 00:00:00. (On EDT you get 19:00:00 for 0). Therefore you need to report in UTC as follows:
$ echo $(date -u -r 110 +%H:%M:%S)
00:01:50
I was unaware of the use of the date command figure elapsed time, but after looking at it I can see that it has a limitation that prevents my using it. As neat as it is, it only works for elapsed times of less than a day. There is no %DAYS option for elapsed days, if you need to monitor things that run that long (or can potentially run that long).
Luckily the shell date arithmetic is pretty simple. You don’t even need ‘bc’.
(( DIFF = NOW – START ))
(( DAYS = DIFF / 86400 ))
(( HOURS = DIFF % 86400 / 3600 ))
(( MIN = DIFF % 3600 / 60 ))
(( SEC = DIFF % 60 ))
ELAP=$(printf “%03d:%02d:%02d:%02d” $DAYS $HOURS $MIN $SEC)
The printf adds leading zeros so the columns don’t float. Not as slick as ‘date -d’, but the code above works for about a year (which should cover anything in real life).
Perfect!
I had a number of scripts that I wanted to track session time on, along with other time variations. I have been able to use all of the information provided here with success and “right-out-of-the-box”.
Finally, I can call some of my scripts completed.
Thanks much!
Had to adjust the last line to get this to work for me — final script below:
#!/bin/bash
before=$(date +%s)
echo Started : $(date)
# commands to time go here
after=$(date +%s)
elapsed_seconds=$(expr $after – $before)
echo Ended : $(date)
echo Elapsed time : $(date -d “1970-01-01 $elapsed_seconds sec” +%H:%M:%S)
Hi iwant to set my server time automaticlly with conecting with ssh to another host ant get correct time and change my server time in crontb enviroment please help me thanks
line:
echo Finished up at $(date -r $after)
should actually read:
echo Finished up at $(date -d $after)
-d displays the time given by input string.
-r displays the time given by reference “file”.
Kept getting “file does not exist error”, found out that the “-d” flag was causing error.
Thanks for the script… saved me a bit of sanity and time… and these days, we need all the sanity we can get.
b@
thanks a lot!! i just grabbed your code, plugged it into my script and now have a timer-based control over my child processes. very useful!
Hi there.
Alternatively, most shells have a ‘time’ facility built-in. This works in bash; your milage may vary for other shells.
———————————-
#!/usr/bin/bash
{ time {
# Your section 1 code goes here
} } 2> timing_section_1.txt
{ time {
# Your section 2 code goes here
} } 2> timing_section_2.txt
———————————-
This will output real, user and cpu timing information into the text files specified.
(The slightly odd-looking code blocks are needed to appropriately redirect standard error to a file. See http://www.cs.tut.fi/~jarvi/tips/bash.html for an explanation.)
Hope this helps.
Regards,
NeilS.