Skip to main content

Bash Shell Slow Start-up

As we install development environments in our system, we may notice that terminal start-up can become slow. Some people notice it, some don't -- but the reality is, it's there.

Finding the culpritโ€‹

This initially started when I was looking at bash start-up time, and that's when I noticed that nvm loading was a bit slow.

Here's how I found out, by printing the time taken to execute commands:

for i in "${HOME}/.bashrc.d"/[0-9][0-9]-*.bashrc; do
if [ -r "$i" ]; then
export TIMEFORMAT="%R $i"
time . "$i"
fi
done
unset i
unset TIMEFORMAT

That gives me this printout when I open a new terminal:

0.051 /home/william/.bashrc.d/00-default.bashrc
0.000 /home/william/.bashrc.d/50-android-sdk.bashrc
0.000 /home/william/.bashrc.d/50-android-studio.bashrc
0.008 /home/william/.bashrc.d/50-asdf.bashrc
0.266 /home/william/.bashrc.d/50-nvm.bashrc
0.037 /home/william/.bashrc.d/50-sdkman.bashrc
0.000 /home/william/.bashrc.d/90-gradle-opts.bashrc

There is another thing that I should check:

export TIMEFORMAT='%R bashrc check'
time if which mktemp >/dev/null 2>&1; then
...

Which gives me:

0.019 bashrc check

So, it seems that nvm takes the longest time, followed by the default bashrc and then SDKMAN!.

The problem with slow loading has been experienced by other people:

Although I'm lucky to now have experienced such slow loading myself.

The solutionโ€‹

Just like most good ideas, others have already thought about it:

But this one looks the most promising.

So I adapted it for my own use:

nvm() {
unset -f nvm node npm
export NVM_DIR="${HOME}/.nvm"
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
[ -s "${NVM_DIR}/bash_completion" ] && . "${NVM_DIR}/bash_completion"
nvm "$@"
}

node() {
unset -f nvm node npm
export NVM_DIR="${HOME}/.nvm"
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
[ -s "${NVM_DIR}/bash_completion" ] && . "${NVM_DIR}/bash_completion"
node "$@"
}

npm() {
unset -f nvm node npm
export NVM_DIR="${HOME}/.nvm"
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
[ -s "${NVM_DIR}/bash_completion" ] && . "${NVM_DIR}/bash_completion"
npm "$@"
}

Now the timings have improved:

0.034 /home/william/.bashrc.d/00-default.bashrc
0.000 /home/william/.bashrc.d/50-android-sdk.bashrc
0.000 /home/william/.bashrc.d/50-android-studio.bashrc
0.002 /home/william/.bashrc.d/50-asdf.bashrc
0.000 /home/william/.bashrc.d/50-nvm.bashrc
0.039 /home/william/.bashrc.d/50-sdkman.bashrc
0.000 /home/william/.bashrc.d/90-gradle-opts.bashrc
0.018 bashrc check

A little snagโ€‹

With this setup, yarn cannot be called without calling nvm or node or npm first, because it's not in the PATH.

No solutions for this yet -- maybe we can do some kind of late-loading instead of lazy-loading.

Or maybe, we can make nvm load faster... somehow?

Anything else?โ€‹

Let's use the same technique for SDKMAN! as well then:

sdk() {
unset -f sdk
export SDKMAN_DIR="${HOME}/.sdkman"
[ -s "${SDKMAN_DIR}/bin/sdkman-init.sh" ] && . "${SDKMAN_DIR}/bin/sdkman-init.sh"
sdk "$@"
}

With this, we have shaved more time off our start-up:

0.060 /home/william/.bashrc.d/00-default.bashrc
0.000 /home/william/.bashrc.d/50-android-sdk.bashrc
0.000 /home/william/.bashrc.d/50-android-studio.bashrc
0.004 /home/william/.bashrc.d/50-asdf.bashrc
0.000 /home/william/.bashrc.d/50-nvm.bashrc
0.000 /home/william/.bashrc.d/50-sdkman.bashrc
0.000 /home/william/.bashrc.d/90-gradle-opts.bashrc
0.018 bashrc check

Great, now we just have to worry about the default .bashrc and the checking part :)

But that's another matter for another day.