How to Measure Bash Script Execution Time

Tested on RHEL 10.2 (Coughlan)
Package bash 5.2.26-6.el10
time 1.9-25.el10
coreutils 9.5-8.el10_2
Applies to Linux distributions with Bash installed
Privilege Normal user
Scope Measure Bash script and command execution time with the time reserved word, $SECONDS, $EPOCHREALTIME, and date +%s; compare Bash time to GNU /usr/bin/time; save timing output to a file. Includes a short timeout example only; signal handling and advanced timeout options are out of scope.
Related guides Bash background process
Bash best practices
Shell scripting course hub
Short on time?Beta Get the gist of this 9-minute read in seconds, then ask follow-up questions

To check how long a Bash script takes, run it under the shell's time reserved word:

bash
time ./myscript.sh

If the file is executable and has a shebang, that is enough. Otherwise call Bash explicitly:

bash
time bash ./myscript.sh

Sample output:

output
done

real	0m2.015s
user	0m0.012s
sys	0m0.002s

real is the total elapsed time from start to finish. user and sys are CPU time in user mode and kernel mode.

When a script waits on disk or network, real is often much larger than user plus sys. On parallel or multi-process work, user plus sys can exceed real because CPU time adds up across cores and child processes—see run shell scripts in parallel for overlapping & jobs timed with the same TIMEFORMAT pattern.


Bash Execution Time Methods Compared

Method Precision Best for
Bash time reserved word Subsecond Time an entire command, pipeline, or script from the shell
$SECONDS Whole seconds Measure phases inside a Bash script
$EPOCHREALTIME Microsecond-granularity timestamp Subsecond elapsed timing in Bash 5+
date +%s Whole seconds Classic shell timestamp difference
/usr/bin/time (GNU Time) Subsecond plus resource stats CPU, memory, and I/O investigation

time, $SECONDS, and $EPOCHREALTIME measure elapsed time. GNU timeout (from coreutils) solves a different problem: stopping a command that runs too long.


Measure a Bash Script with time

Bash provides time as a reserved word so it can time pipelines, builtins, and shell functions. It is not the same program as /usr/bin/time.

For example:

bash
time sleep 1

Sample output:

output
real	0m1.005s
user	0m0.004s
sys	0m0.000s

Bash writes the timing summary to stderr after the timed command finishes. Because time is shell syntax, it can time pipelines, brace groups, and shell functions that GNU /usr/bin/time cannot see directly.


Format Bash time Output with TIMEFORMAT

For POSIX-style output, Bash also accepts time -p:

bash
time -p sleep 1

Sample output:

output
real 1.01
user 0.00
sys 0.00

-p uses Bash's POSIX timing format instead of the format specified by TIMEFORMAT.

Set TIMEFORMAT before you use the Bash time keyword for a custom layout. This variable does not affect /usr/bin/time:

bash
TIMEFORMAT='real %3R sec (user %3U sys %3S)'
time sleep 1

Sample output:

output
real 1.006 sec (user 0.003 sys 0.001)

%R is real seconds, %U is user CPU, and %S is sys CPU. Run help TIMEFORMAT in Bash for the full format list.


Time Pipelines, Functions, and Command Groups

Brace groups run as one timed unit:

bash
time { sleep 0.5; echo done; }

Sample output:

output
done

real	0m0.506s
user	0m0.004s
sys	0m0.001s

A pipeline is timed as a whole:

bash
time echo hello | wc -c

Sample output:

output
6

real	0m0.006s
user	0m0.005s
sys	0m0.001s

To time a shell function, call it in the same shell where time runs. GNU /usr/bin/time cannot do that unless you wrap the call in bash -c or export the function to a subshell.


Save Bash time Output to a File

Bash writes its timing summary to stderr, not stdout. Group the timed command so a stderr redirect catches the timing report:

bash
{ time ./myscript.sh; } 2> timing.log

The braces group the timed command so the 2> redirection also applies to Bash's timing report. That redirect captures stderr from the script too. If ./myscript.sh prints echo "ERROR" >&2, timing.log contains that line as well as the real / user / sys summary. When you need timing statistics in their own file, GNU /usr/bin/time -o is simpler.

On RHEL, GNU Time comes from the time package, not coreutils:

bash
/usr/bin/time -o gnu-timing.log -f '%e' sleep 1

The command's own stdout and stderr stay on the terminal; only the formatted stats go to the file. Read them with:

bash
cat gnu-timing.log

Sample output:

output
1.00

With the Bash group redirect, read the mixed stderr log like this:

bash
cat timing.log

Sample output:

output
real	0m2.021s
user	0m0.009s
sys	0m0.005s

Bash time vs /usr/bin/time

type -a time on a typical Linux host shows both the keyword and the external program:

bash
type -a time

Sample output:

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

Bash runs its keyword when you write time cmd in an interactive shell or script. To force GNU Time, call it by path:

bash
/usr/bin/time -f 'real %e user %U sys %S maxrss %M KB' sleep 1

Sample output:

output
real 1.00 user 0.00 sys 0.00 maxrss 1892 KB

Install GNU Time separately on RHEL if /usr/bin/time is missing. Package installs use dnf command:

bash
sudo dnf install time
Feature Bash time GNU /usr/bin/time
Directly time a pipeline Yes No; wrap it in bash -c '...'
Directly time a shell function Yes No; requires another shell or export
Resource and memory statistics Limited Yes with -f or -v
Format output TIMEFORMAT -f
Write stats to a file Redirect stderr (2>) -o / --output

Use Bash time for shell-heavy work. Use GNU Time when you also need memory or I/O statistics.


Measure Elapsed Time Inside a Script with SECONDS

Bash's SECONDS variable reports the number of seconds since the shell started. Assigning a value resets the reference point, so SECONDS=0 is convenient for elapsed-time measurement:

bash
SECONDS=0
sleep 2
echo "SECONDS=$SECONDS"

Sample output:

output
SECONDS=2

The value is an integer. You get whole seconds only, which is enough for coarse phase timing in maintenance scripts.

You can reset SECONDS before each stage of a script:

bash
SECONDS=0
echo "backup start"
sleep 1
echo "backup phase took ${SECONDS}s"

Sample output:

output
backup start
backup phase took 1s

To print hours, minutes, and seconds, divide the elapsed value:

bash
SECONDS=0
sleep 65
duration=$SECONDS
printf 'Elapsed: %02d:%02d:%02d\n' \
  $((duration / 3600)) \
  $(((duration % 3600) / 60)) \
  $((duration % 60))

Sample output:

output
Elapsed: 00:01:05

Reassign SECONDS=0 before each phase when you want per-step durations in one script. The variable is per shell instance; subshells inherit but do not share updates back to the parent.


Measure Subsecond Time with EPOCHREALTIME

Bash 5.0 and later expose EPOCHREALTIME as seconds since the Unix epoch with a fractional part at microsecond granularity. Shell arithmetic is integer-only, so subtract start and end in awk for a decimal elapsed time:

bash
start=$EPOCHREALTIME
sleep 0.2
awk -v s="$start" -v e="$EPOCHREALTIME" 'BEGIN {printf "%.3f\n", e-s}'

Sample output:

output
0.206

The awk command handles the floating-point subtraction Bash cannot do in $(( )). bc can perform the same subtraction if your script already depends on it.

To print the elapsed time whenever the shell exits, register an EXIT trap:

bash
start=$EPOCHREALTIME
report_duration() {
  awk -v s="$start" -v e="$EPOCHREALTIME" 'BEGIN {printf "Elapsed: %.2fs\n", e-s}' >&2
}
trap report_duration EXIT
sleep 1
echo "work done"

Sample output:

output
work done
Elapsed: 1.01s

The EXIT trap runs when the shell exits, so the elapsed-time message still prints when the script finishes with a non-zero status. See Bash trap for other EXIT-trap patterns.


Measure Elapsed Time with date +%s

Another common approach is to record Unix timestamps before and after a command:

bash
start=$(date +%s)
sleep 2
end=$(date +%s)
echo $((end - start))

Sample output:

output
2

This pattern works in shells where the installed date utility supports %s. On Bash, $SECONDS is usually simpler because you do not need two variables and a subtraction.

GNU date +%s%N prints epoch seconds followed by the nanosecond field, which can be used for subsecond timing. BSD date and POSIX sh do not guarantee %N. Treat %N as a GNU/Linux shortcut, not a portable standard.


Accuracy and Portability Notes

  • $SECONDS is Bash-specific and reports whole seconds only.
  • $EPOCHREALTIME needs Bash 5+ and follows the system wall clock. NTP steps can make a delta look odd; the timestamp has microsecond granularity, not guaranteed microsecond measurement accuracy.
  • date +%s is widely available for second-level deltas; %N is not portable.
  • Use wall-clock timing for script logs and troubleshooting; pair timing with trace debugging in Debug a Bash script when elapsed time alone does not explain a hang. Use a dedicated benchmark tool (hyperfine, perf, or a language-specific harness) for performance comparisons.

Limit Script Execution Time with timeout

timeout does not measure execution time; it stops a command after a specified duration. For wall-clock loops, EPOCHSECONDS, and exit 124 handling in more depth, see Bash while loop until a deadline:

bash
timeout 30s ./myscript.sh

With the default settings, GNU timeout sends SIGTERM when the duration expires and returns status 124 for a timeout:

bash
timeout 5s sleep 10
echo "exit=$?"

Sample output:

output
exit=124

Summary

Check Bash script execution time from the shell with time ./script.sh or time bash script.sh. Read real for wall-clock elapsed time; user and sys show CPU usage, which can be lower or higher than real depending on I/O waits and parallelism.

Inside a script, $SECONDS gives integer elapsed seconds, which you can convert to HH:MM:SS. $EPOCHREALTIME with awk supports subsecond elapsed timing on Bash 5 and later. date +%s remains a widely supported second-level timestamp pattern. GNU /usr/bin/time from the time package adds resource statistics and -o for log files; Bash time stays inside the shell and handles pipelines and functions directly.

Use these methods for script logging and troubleshooting. Use a dedicated benchmark tool when comparing performance. To enforce a maximum runtime instead of measuring elapsed time, use timeout 30s ./script.sh, which exits 124 when the limit is hit.


References


Frequently Asked Questions

How do I check how long a Bash script takes to run?

Run time ./script.sh when the file is executable, or time bash ./script.sh otherwise. The real line on stderr is elapsed wall-clock time.

What do real, user, and sys mean in Bash time output?

real is wall-clock elapsed time from start to finish. user is CPU time in user mode and sys is CPU time in kernel mode. I/O waits inflate real; parallel work can make user plus sys exceed real.

How do I measure milliseconds in a Bash script?

On Bash 5+, read $EPOCHREALTIME at the start and end, then subtract in awk because shell arithmetic is integer-only. date +%s%N works on many GNU/Linux systems but is not portable to BSD or POSIX sh.

How do I save Bash time output to a variable or file?

Bash writes timing to stderr. { time ./script.sh; } 2> timing.log saves the timing report, but the same redirect also captures stderr from the script. GNU /usr/bin/time -o file writes statistics to a separate file without mixing script output.

What is the difference between Bash time and /usr/bin/time?

Bash time is a reserved word that can time pipelines, builtins, and shell functions in the current shell. /usr/bin/time is GNU Time, a separate program that reports resource statistics but must wrap shell syntax in bash -c when needed.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)