Count process spawns in a script
Count how many processes a shell script starts with strace -f -c -e trace=execve,clone. A loop that calls date five times runs six execs, and a builtin runs one.
A script that loops over many items is slow, and the suspicion is that it starts a new process for every item.
cat > external.sh <<'EOF'
for i in 1 2 3 4 5; do t=$(date +%s); done
EOF
cat > builtin.sh <<'EOF'
for i in 1 2 3 4 5; do printf -v t '%(%s)T' -1; done
EOF
for s in external.sh builtin.sh; do
echo "$s:"
strace -f -qq -c -e trace=execve,clone,clone3 bash $s 2>&1 | awk '$NF=="execve" || $NF=="clone" || $NF=="clone3" {print " " $4, $NF}'
doneexternal.sh:
5 clone
6 execve
builtin.sh:
1 execvestrace -f follows child processes, -c prints a table of the calls instead of each one, and -e trace= limits the table to process creation, which is clone and execve on Linux. The command substitution in the first script forks a subshell for every pass and then runs date, so the five passes give five forks and six execs, counting the shell itself. The second script uses the %()T format of the printf builtin and runs a single exec. See the strace manual.
strace slows the traced program down, so use it to count calls and not to time them. It needs permission to trace, which containers and some hardened kernels deny with an error about ptrace. On macOS the equivalents are dtruss or fs_usage. %()T needs Bash 4.2 or later.