opscape

Processes and jobs · bash builtin

time

Measure how long a command takes (wall-clock, user and system CPU time); GNU /usr/bin/time also reports memory and I/O.

time [-p] PIPELINE   or   /usr/bin/time [-v] [-f FORMAT] [-o FILE] COMMAND [ARG...]

12 of 12 examples run in Debian 13.7 on 2026-10-31 · manual: time(1)

There are two different things called time. In bash (and zsh, ksh), time is a shell keyword: it times the whole pipeline after it, including shell functions, loops and { ...; } groups, and prints real, user and sys on standard error. Its format is controlled by the TIMEFORMAT variable. There is also a separate program, GNU time, at /usr/bin/time (Debian package time), which can only run an external command but reports much more: maximum resident memory, page faults, context switches and file-system I/O, in a format you choose with -f, written to a file with -o.

The three numbers: real is elapsed wall-clock time; user is CPU time spent in the program's own code; sys is CPU time spent in the kernel on its behalf. A program that waits on the network or sleep has a large real and tiny user/sys; a multi-threaded program can show more user than real, because several cores were busy at once.

Typing time cmd at a bash prompt always gets the keyword. To get GNU time, write the full path /usr/bin/time, or command time / \time, which bypass the keyword. sh (dash) has no time keyword, so there time runs /usr/bin/time.

Examples

Time a command

time sleep 0.5
explain
output

real	0m0.502s
user	0m0.000s
sys	0m0.001s

real is about half a second of elapsed time; user and sys are near zero because sleep waits without using the CPU. The report goes to standard error, after the command's own output.

Compare wall-clock and CPU time

time python3 -c 'sum(i * i for i in range(3_000_000))'
explain
output

real	0m0.164s
user	0m0.159s
sys	0m0.005s

A CPU-bound command: almost all of real is user time. If real were much larger than user + sys, the program would be waiting on I/O, the network or other processes.

Check which time you are running

type time
type -a /usr/bin/time
output
time is a shell keyword
/usr/bin/time is /usr/bin/time

In bash, time is a keyword, parsed before the command runs, which is why it can time pipelines and functions. GNU time is a normal program on disk.

Time a whole pipeline or block

time (sleep 0.2 | sleep 0.3)
time { for i in 1 2 3; do sleep 0.1; done; }
output

real	0m0.303s
user	0m0.000s
sys	0m0.005s

real	0m0.306s
user	0m0.002s
sys	0m0.004s

The keyword times everything after it until the end of the pipeline: here a subshell holding a pipeline (0.3 s, the slower stage) and a loop in braces (three 0.1 s sleeps).

Change the output format

TIMEFORMAT='took %3R s (user %3U s, sys %3S s, %P%% CPU)'
time sleep 0.3
output
took 0.303 s (user 0.000 s, sys 0.003 s, 0.85% CPU)

TIMEFORMAT sets the keyword's report: %R, %U and %S are real, user and sys seconds, the digit is the number of decimals, %P the CPU percentage. time -p instead gives the POSIX format, three lines real, user, sys.

Capture the timing in a variable or file

TIMEFORMAT=%R
{ time sleep 0.2 > /dev/null; } 2> elapsed.txt
echo "elapsed: $(cat elapsed.txt) s"
output
elapsed: 0.203 s

The keyword writes to the shell's standard error, not the command's, so time cmd 2> file does not capture it. Wrap the timed command in { ...; } and redirect the group.

GNU time's default report

/usr/bin/time sort access.log > /dev/null
explain
output
0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 1152maxresident)k
12inputs+0outputs (0major+106minor)pagefaults 0swaps

One dense line: user and system seconds, elapsed time, CPU percentage, then memory (maxresident is peak memory in KiB), file-system inputs and outputs in 512-byte blocks, and page faults.

Full resource report with -v

/usr/bin/time -v python3 -c 'x = bytearray(80_000_000)' 2>&1 | head -12
explain
output
	Command being timed: "python3 -c x = bytearray(80_000_000)"
	User time (seconds): 0.01
	System time (seconds): 0.05
	Percent of CPU this job got: 100%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.06
	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): 84480
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 0

-v prints one labeled line per measurement. Maximum resident set size is the figure people usually want: the peak memory the program used, here about 80 MB plus Python itself. The output is cut at 12 lines with head; the full report also covers context switches, file-system I/O and the exit status.

Pick the fields with -f

/usr/bin/time -f '%e s elapsed, %M KiB peak, exit %x' gzip -k -9 access.log
explain
output
0.00 s elapsed, 768 KiB peak, exit 0

-f takes a format: %e elapsed seconds, %M peak resident memory in KiB, %x the exit status, %U/%S user and system time, %C the command line. Ideal for logging the cost of batch jobs.

Write measurements to a file

for f in data/users.csv data/sales.csv; do
  /usr/bin/time -a -o times.log -f "%e s  %M KiB  %C" sort "$f" > /dev/null
done
cat times.log
output
0.00 s  1152 KiB  sort data/users.csv
0.00 s  1152 KiB  sort data/sales.csv

-o FILE writes the report to a file instead of standard error, so it does not mix with the command's own messages, and -a appends instead of overwriting.

The exit status passes through

/usr/bin/time -f '%e s' ls missing.txt
echo "exit status: $?"
output
ls: cannot access 'missing.txt': No such file or directory
Command exited with non-zero status 2
0.00 s
exit status: 2

GNU time reports Command exited with non-zero status 2 before its own line and then exits with the command's status, so it can wrap commands in scripts without hiding failures. The bash keyword also preserves the status.

GNU time cannot time functions or builtins

build() { sleep 0.1; }
/usr/bin/time -f %e build
echo "exit status: $?"
output
/usr/bin/time: cannot run build: No such file or directory
Command exited with non-zero status 127
0.00
exit status: 127

/usr/bin/time executes a program from PATH, so it does not know the shell function and fails with status 127. Use the keyword (time build) for functions and pipelines.

Common pitfalls

  • Expecting time cmd > out 2> err to put the timing in err. The bash keyword reports on the shell's standard error; use { time cmd; } 2> err.

  • Reaching for /usr/bin/time -v on a minimal system where it is not installed (Debian package time). The bash keyword is always there, but it does not measure memory.

  • Timing something once and drawing conclusions. Caches, CPU frequency scaling and other load change results; run it several times, or use a benchmarking tool such as hyperfine for serious comparisons.

  • Reading user greater than real as an error. It means the program used several cores in parallel.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • macOS /usr/bin/time is the BSD version: no -f or -v. Use -l for a resource report including maximum resident set size (in bytes, not KiB) and -p for POSIX output. GNU time is available from Homebrew as gtime.

  • zsh's time keyword prints a different one-line format, controlled by TIMEFMT rather than bash's TIMEFORMAT.

Options worth knowing

A selection, described in our own words. For the full list, see time(1).

OptionWhat it does
-p, --portabilityUse the POSIX output format: real, user and sys on separate lines.
-v, --verboseGNU time: print a detailed, labeled resource report.
-f, --format FORMATGNU time: print the report using FORMAT (%e elapsed, %M peak KiB, %x exit status, ...).
-o, --output FILEGNU time: write the report to FILE instead of standard error.
-a, --appendGNU time: with -o, append to FILE instead of overwriting it.
-q, --quietGNU time: do not report abnormal termination or non-zero exit status.

Related