417

I want to execute something in a linux shell under a few different conditions, and be able to output the execution time of each execution.

I know I could write a perl or python script that would do this, but is there a way I can do it in the shell? (which happens to be bash)

ʞɔıu
  • 43,326
  • 30
  • 94
  • 142

10 Answers10

546

Use the built-in time keyword:

$ help time

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:

$ time sleep 2
real    0m2.009s
user    0m0.000s
sys     0m0.004s
mklement0
  • 245,023
  • 45
  • 419
  • 492
Robert Gamble
  • 97,930
  • 23
  • 141
  • 137
  • 1
    How is this used on a command like `time -p i=x; while read line; do x=x; done < /path/to/file.txt`? It immediatly returns 0.00 unless I don't put anything before the while loop.. what gives? – natli Dec 14 '12 at 21:45
  • this is poor 'time' version, bash builtin. where is external 'time' command? – Znik Jan 02 '14 at 13:37
  • 7
    @Znik, try /usr/bin/time – Mark Rajcok Jun 22 '15 at 21:08
  • 11
    @natli: While `time` can time an entire pipeline as-is (by virtue of being a Bash _keyword_), you need to use a _group command_ (`{ ...; ...; }`) to time _multiple_ commands: `time -p { i=x; while read line; do x=x; done < /path/to/file.txt; }` – mklement0 Oct 30 '16 at 03:55
  • You can measure like this time of piped commands and scripts, i.e. time `ls | grep MyFolder` or our scripts scripts, i.e. time myScript.sh – Do-do-new Feb 23 '17 at 13:57
  • 4
    To see all versions of time you have installed on your system, you can use `type -a time` – spinup Apr 02 '18 at 14:36
129

You can get much more detailed information than the bash built-in time (which Robert Gamble mentions) using time(1). Normally this is /usr/bin/time.

Editor's note: To ensure that you're invoking the external utility time rather than your shell's time keyword, invoke it as /usr/bin/time.
time is a POSIX-mandated utility, but the only option it is required to support is -p.
Specific platforms implement specific, nonstandard extensions: -v works with GNU's time utility, as demonstrated below (the question is tagged ); the BSD/macOS implementation uses -l to produce similar output - see man 1 time.

Example of verbose output:

$ /usr/bin/time -v sleep 1
       Command being timed: "sleep 1"
       User time (seconds): 0.00
       System time (seconds): 0.00
       Percent of CPU this job got: 1%
       Elapsed (wall clock) time (h:mm:ss or m:ss): 0:01.05
       Average shared text size (kbytes): 0
       Average unshared data size (kbytes): 0
       Average stack size (kbytes): 0
       Average total size (kbytes): 0
       Maximum resident set size (kbytes): 0
       Average resident set size (kbytes): 0
       Major (requiring I/O) page faults: 0
       Minor (reclaiming a frame) page faults: 210
       Voluntary context switches: 2
       Involuntary context switches: 1
       Swaps: 0
       File system inputs: 0
       File system outputs: 0
       Socket messages sent: 0
       Socket messages received: 0
       Signals delivered: 0
       Page size (bytes): 4096
       Exit status: 0
Peter Vandivier
  • 421
  • 4
  • 20
grepsedawk
  • 5,653
  • 5
  • 21
  • 21
  • 2
    you wouldn't happen to know what debian package that would come from? doesn't seem to be installed by default – ʞɔıu Dec 22 '08 at 02:42
  • 1
    I was referencing your post Robert. Unless you mean the command you suggest is not the bash built-in? – grepsedawk Dec 22 '08 at 02:50
  • 25
    Amazingly enough, it's installed from a package called "time". – Paul Tomblin Dec 22 '08 at 02:53
  • 2
    The output from the /usr/bin/time looks like "0.00user 0.00system 0:02.00elapsed 0%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+172minor)pagefaults 0swaps" – Paul Tomblin Dec 22 '08 at 02:54
  • 1
    @grepsedawk: The time command I used was the bash built-in command, the time(1) command which you provided the man page for is not the built-in command but a separate standalone program, I thought you were confusing the two. – Robert Gamble Dec 22 '08 at 03:12
  • 4
    @Nick: "sudo apt-get install time". – Robert Gamble Dec 22 '08 at 03:13
  • 1
    Anyway, +1 because I never used the /usr/bin/time because of the ugly output but now I know how to get nicely formatted output and it provides a lot of other useful information (which I wouldn't expect from a command called "time"). – Robert Gamble Dec 22 '08 at 03:17
  • 1
    This will not permit to time shell constructs as, e.g., `time { sleep 10; echo hi; sleep 10; }`. Only the shell's `time`r will ever be able to do that. You could argue that `/usr/bin/time bash -c 'sleep 10; echo hi; sleep 10;'` would then work. Fair enough `:)`. – gniourf_gniourf Oct 31 '13 at 16:52
  • Any linux guru know how to use this with a redirect ie. `/usr/bin/time -v make > output.log`? – puk Feb 03 '18 at 21:29
  • Why the crap does `which time` give me `/usr/bin/time`, but bash's time gets called anyway by `time`? (Specifying the full path in the command gives the correct result, as you advised.) – geofurb Nov 28 '18 at 22:06
  • @puk - try this: `/usr/bin/time -v make &> output.log` – slm May 08 '19 at 03:06
  • `where time` time: shell reserved word `/usr/bin/time` – jasonleonhard May 19 '21 at 00:48
  • Thus you can replace `/usr/bin/time` with just the word `time` – jasonleonhard May 19 '21 at 00:49
85
#!/bin/bash
START=$(date +%s)
# do something
# start your script work here
ls -R /etc > /tmp/x
rm -f /tmp/x
# your logic ends here
END=$(date +%s)
DIFF=$(( $END - $START ))
echo "It took $DIFF seconds"
David
  • 1,025
  • 9
  • 2
  • 15
    There is much simpler way. Bash calculate automaticly special variable $SECONDS , then recalculation based on external date command is unneeded. $SECONDS variable keeps how many seconds bash script is running when it starts. This variable has some special property. See man page :D – Znik Jun 29 '15 at 12:02
  • I have tried both the above & the method in the comment. In the 1st I get an 'illegal variable' error & the 2nd I get 'unidentified variable' – DrBwts May 05 '16 at 12:19
46

For a line-by-line delta measurement, try gnomon.

A command line utility, a bit like moreutils's ts, to prepend timestamp information to the standard output of another command. Useful for long-running processes where you'd like a historical record of what's taking so long.

You can also use the --high and/or --medium options to specify a length threshold in seconds, over which gnomon will highlight the timestamp in red or yellow. And you can do a few other things, too.

example

abathur
  • 1,008
  • 7
  • 18
Adriano Resende
  • 2,149
  • 1
  • 24
  • 29
  • 3
    Great tip, but just to be clear: this is useful for _line-by-line_ timings, but the overhead of taking such fine-grained timings significantly adds to the _overall_ timing. – mklement0 Oct 30 '16 at 04:17
  • 2
    Awesome utility.. Thanks for sharing – Kishan B Feb 02 '18 at 12:01
20

Should you want more precision, use %N with date (and use bc for the diff, because $(()) only handles integers).

Here's how to do it:

start=$(date +%s.%N)
# do some stuff here
dur=$(echo "$(date +%s.%N) - $start" | bc)

printf "Execution time: %.6f seconds" $dur

Example:

start=$(date +%s.%N); \
  sleep 0.1s; \
  dur=$(echo "$(date +%s.%N) - $start" | bc); \
  printf "Execution time: %.6f seconds\n" $dur

Result:

Execution time: 0.104623 seconds
Benoit Duffez
  • 9,889
  • 11
  • 69
  • 114
16

If you intend to use the times later to compute with, learn how to use the -f option of /usr/bin/time to output code that saves times. Here's some code I used recently to get and sort the execution times of a whole classful of students' programs:

fmt="run { date = '$(date)', user = '$who', test = '$test', host = '$(hostname)', times = { user = %U, system = %S, elapsed = %e } }"
/usr/bin/time -f "$fmt" -o $timefile command args...

I later concatenated all the $timefile files and pipe the output into a Lua interpreter. You can do the same with Python or bash or whatever your favorite syntax is. I love this technique.

Norman Ramsey
  • 188,173
  • 57
  • 343
  • 523
13

If you only need precision to the second, you can use the builtin $SECONDS variable, which counts the number of seconds that the shell has been running.

while true; do
    start=$SECONDS
    some_long_running_command
    duration=$(( SECONDS - start ))
    echo "This run took $duration seconds"
    if some_condition; then break; fi
done
glenn jackman
  • 207,528
  • 33
  • 187
  • 305
11

You can use time and subshell ():

time (
  for (( i=1; i<10000; i++ )); do
    echo 1 >/dev/null
  done
)

Or in same shell {}:

time {
  for (( i=1; i<10000; i++ )); do
    echo 1 >/dev/null
  done
}
Eduardo Cuomo
  • 13,985
  • 3
  • 93
  • 80
  • You posted two identical code snippets. You must have meant to have something different there. – stason Aug 16 '18 at 01:15
  • 5
    @StasBekman is not true, first using `(` & `)` (new context, _subshell_), and other with `{` & `}` (same shell, same context) – Eduardo Cuomo Aug 16 '18 at 19:46
  • Aha! I was looking really hard at the two and didn't see the difference. Thank you for clarifying that {} vs (). – stason Aug 17 '18 at 05:45
2

The way is

$ > g++ -lpthread perform.c -o per
$ > time ./per

output is >>

real    0m0.014s
user    0m0.010s
sys     0m0.002s
palacsint
  • 26,486
  • 10
  • 75
  • 100
Robel Sharma
  • 917
  • 1
  • 10
  • 26
  • No need to use `-lphtread` or `-o` tags. Just need to use the `time` command, which the accepted answer explains better. – Dennis Apr 08 '12 at 04:35
  • 7
    It was an example .That is my perform.c has threading so I need -lpthread and for simple identity it is -o per. – Robel Sharma Apr 12 '12 at 05:38
0

one possibly simple method ( that may not meet different users needs ) is the use of shell PROMPT.it is a simple solution that can be useful in some cases. You can use the bash prompting feature as in the example below:

export PS1='[\t \u@\h]\$' 

The above command will result in changing the shell prompt to :

[HH:MM:SS username@hostname]$ 

Each time you run a command (or hit enter) returning back to the shell prompt, the prompt will display current time.

notes:
1) beware that if you waited for sometime before you type your next command, then this time need to be considered, i.e the time displayed in the shell prompt is the timestamp when the shell prompt was displayed, not when you enter command. some users choose to hit Enter key to get a new prompt with a new timestamp before they are ready for the next command.
2) There are other available options and modifiers that can be used to change the bash prompt, refer to ( man bash ) for more details.

tstorym
  • 1
  • 1