40

I would like to do the following at one point in a script:

start_time=date

and this after a process or processes have run:

end_time=date

and then do this:

elapsed=$end_time-$start_time
echo "Total of $elapsed seconds elapsed for process"

How would I do this?

5 Answers5

46

Use the time since epoch to easily identify a span of time in a script

man date
%s     seconds since 1970-01-01 00:00:00 UTC
%N     nanoseconds (000000000..999999999)

.

start_time="$(date -u +%s)"
sleep 5
end_time="$(date -u +%s)"

elapsed="$(($end_time-$start_time))"
echo "Total of $elapsed seconds elapsed for process"

 Total of 5 seconds elapsed for process

Bash doesn't support floating point numbers, so you'll need to use a external tool like bc to compare times like 1475705058.042270582-1475705053.040524971

start_time="$(date -u +%s.%N)"
sleep 5
end_time="$(date -u +%s.%N)"

elapsed="$(bc <<<"$end_time-$start_time")"
echo "Total of $elapsed seconds elapsed for process"

 Total of 5.001884264 seconds elapsed for process
Miati
  • 3,150
36

bash has a builtin timer variable

start=$SECONDS
# do stuff
end=$SECONDS
duration=$(( end - start ))
echo "stuff took $duration seconds to complete"
glenn jackman
  • 85,964
12

@jasonwryan already suggested it, but I'll throw it in as an answer as it is also my go-to when I want to time a script. To time myscript simply use:

time myscript
sam
  • 22,765
  • 2
    I will try this from time to time but this is more for when I need to isolate or time sub-sections of my script.. – Oliver Williams Oct 05 '16 at 20:38
  • 1
    This is not useful if you want to use the derived elapsed time within the script itself, e.g. to calculate how many units the script was able to process per second. – Hans Ekbrand Jul 13 '20 at 10:25
1

I like @Miata's answer because it draws attention to date +%s solution. However, @Miata includes .%N but I am not sure what benefit you will get looking at nanoseconds when there is the cost of executing the date command in a subshell. So, in practice, date +%s should be sufficient. The other thing to note is date +%s epoch is 1970-01-01 00:00:00 UTC. Which means the time has an absolute epoch and you can share the time measurement between different instances of shell script or different machines.

(( start=$(date +%s) )) # 1604026743
# ... some activity that lasts for, say, 24 seconds
(( end=$(date -u +%s) )) # 1604026767
(( duration=end-start )) # 24

I like @glenn jackman's answer because it refers to SECONDS bash built-in variable. The thing to note here is the epoch is the time when the process was started. So, we can only use this technique to measure times for things that happen within that script:

(( start=SECONDS )) # 10
# ... some activity that lasts for, say, 24 seconds
(( end=SECONDS )) # 34
(( duration=end-start )) # 24

Another thing is I put all my assignments within the (( )) command. i.e. you can rewrite:

cmd=$(( some_expression ))

as

(( cmd=some_expression ))

By doing this, we can minimize the occurrence of the $ symbol.

1

It's terribly annoying to read the answers in terms of seconds, when you're measuring something that might take a long time. You really want to get the answer format right (as hh:mm:ss) without contorted calculations. Use date own functionality, like this:

# Using differences, and give result in hh:mm:ss
S=0; E=121; TT=$((E-S)); date -u -d "@${TT}" +%T
# 00:02:01

Using date differences, and give result in hh:mm:ss

S=$(date -u +"%s"); E=$((S+121)); TT=$((E-S)); date -u -d "@${TT}" +%T

00:02:01

not2qubit
  • 1,666