I have been growing my bashrc bits by bits for years now, and coming back to an older computer made me realize how slow spawning a new terminal is.
Here is how I was able to bring down my 2 seconds terminal boot time to below 200ms.
With bash, you have:
set -x to tells what runs and where,
PS4 to inject text for debuglines,
${EPOCHREALTIME} to display timestamps in PS4
So basically, the profiling happens by:
Save this as /tmp/profile-bashrc-xtrace.sh:
#!/usr/bin/env bash
# Open a dedicated FD for xtrace output (robust against FD reuse)
exec {__XFD}>"/tmp/bashrc-xtrace.$$".log
export BASH_XTRACEFD=$__XFD
# Prefix each traced command with:
# +<timestamp> <file>:<line>:
export PS4='+${EPOCHREALTIME} ${BASH_SOURCE}:${LINENO}: '
# Enable tracing and source bashrc
set -x
source ~/.bashrc
set +x
# Close trace FD
exec {__XFD}>&-
Run it with a clean shell:
bash --noprofile --norc -i /tmp/profile-bashrc-xtrace.sh
This produces a log like:
+1702901234.421391 /home/me/.bashrc:1380: stow ...
+1702901234.567842 /home/me/.bashrc:1471: curl ...
This analyzer:
, VS . defensively,
awk '
/^\+/ {
line=$0
sub(/^\+/, "", line)
# Extract timestamp (first token only)
t=line
sub(/[ \t].*$/, "", t)
gsub(/,/, ".", t)
loc=$2
cmd=""
for (i=3; i<=NF; i++) cmd = cmd $i (i<NF ? " " : "")
if (prev_t != "") {
dt = (t - prev_t) * 1000
if (dt >= 0)
printf "%.1f ms\t%s\t%s\n", dt, prev_loc, prev_cmd
}
prev_t=t; prev_loc=loc; prev_cmd=cmd
}
' "/tmp/bashrc-xtrace.*.log" | sort -nr | head -30
What showed up at the top:
Things to try to solve them (YMMV):