This code needs to be 100% POSIX-compliant for I want people to be able to run it in virtually any shell. You see correctly, no she-bang (default interpreter is unset = the first line is blank). I spent two hours on getting it in better shape, than it has been years ago. It now prints the iteration number on Ctrl+c by using trap, disables Ctrl+z suspend, can be given command-line argument = number of iterations, includes representative results produced on my laptop = look at the very bottom, prints out the interpreter. Primarily, I re-wrote this script of mine for a final touch, which would, in a fun way show the speed of the interpreter's built-in variables vs calling an external program, id in here.
# It is important you do not use the `errexit` option for benchmarking purposes!
set -o nounset
# Disable suspend functionality, usually invoked by pressing Ctrl+Z.
trap '' TSTP
# Reduce errors when Ctrl+C is pressed to produce current iteration.
trap '[ ${i+set} = set ] && echo $i' INT
# ~~~~~~~~CORRECT USAGE~~~~~~~~
# 1. Intentionally, the file does not need to have an executable bit, nor a shebang!
# 2. To use/run it, call the file directly with your shell interpreter, for example:
#
# On modern shells, this should take a fraction of one second
# (reading $EUID built-in shell variable is extremely fast)
# bash is_user_root__benchmark
#
# POSIX could take couple seconds
# (depends on a CPU, as calling an external program /usr/bin/id here is very expensive)
# dash is_user_root__benchmark
# ~~~~~~~~THE CORE FUNCTION~~~~~~~~
# 1. It is completely safe to disable the ShellCheck SC3028 warning,
# because if $EUID variable is undefined (or empty), id is called.
# 2. Additionally, if using errexit shell option, we must not return
# false value, which ordinary user ID would trigger here, expensive
# workaround on this issue would be e.g. setting `echo TRUE` in case
# the script is running as root, and in the below `while` loop compare
# the returned value, but as said it is very expensive operation,
# if repeated thousands of times... Hence my recommendation on 1st line
is_user_root() {
# shellcheck disable=SC3028
[ "${EUID:-$(id -u)}" -eq 0 ]
}
# ~~~~~~~~BENCHMARK / SHOW-OFF~~~~~~~~
# Repeat the is_user_root() function invokes $iterations-times.
# Default number of is_user_root() function calls, you can customize if taking too long.
# You may also call the script with a number argument indicating iteration number.
if [ "$#" -eq 0 ]
then
readonly iterations=10000
else
readonly iterations=$1
fi
interpreter=$(readlink -f /proc/$$/exe)
printf '%s\n' "${iterations}x is_user_root() in $(basename "$interpreter")"
print_time() {
date +"%T.%2N"
}
printf '%s' 'Start : '; print_time
i=1; while [ "$i" -le "$iterations" ]; do
is_user_root
i=$((i + 1))
done
printf '%s' 'Finish: '; print_time
# ~~~Bash results on AMD Ryzen 9 7845HX~~~
# $ bash is_user_root__benchmark 100000
# 100000x is_user_root() in bash
# Start : 00:49:06.46
# Finish: 00:49:07.17
# $ bash is_user_root__benchmark 1000000
# 1000000x is_user_root() in bash
# Start : 00:49:21.69
# Finish: 00:49:28.63
# ~~~Dash results on AMD Ryzen 9 7845HX~~~
# $ dash is_user_root__benchmark 1000
# 1000x is_user_root() in dash
# Start : 00:54:33.83
# Finish: 00:54:34.48
# $ dash is_user_root__benchmark 10000
# 10000x is_user_root() in dash
# Start : 00:54:44.00
# Finish: 00:54:50.48
EUID=0 dash is_user_root__benchmark\$\endgroup\$/usr/bin/idrepeatedly... \$\endgroup\$