Avoid sourcing scripts multiple times

At the moment, when I open a terminal on my Mac, it automatically calls:

source ~/.bash_profile.

What bemuses me, however, is that my shell does not seem to inherit any of the content from ~/.bashrc or ~/.profile. But that might be typical.

I have a number of scripts to inherit using source or .; if I put those calls in ~/.bash_profile, it can take some time to load a new shell window, sometimes 3 or 4 seconds, and that gets old. I imagine that there is some way to source those scripts just once and have my ~/.bash_profile file inherit it somehow.

Note that calling source ~/.bashrc or source ~/.profile from my ~/.bash_profile is not what I want to do, and is also probably a bad idea. It’s not what I want to do because it doesn’t solve the problem of a slow load for each new shell.

I added some echo statements; every time I open a new bash terminal window, this is logged:

starting to load /etc/profile
finished loading /etc/profile
starting to load bash_profile
finished loading bash_profile

that makes sense, but what is disheartening – do I really need to load these every time a new shell is opened? Why can’t it do some fancy inheritance so that we don’t have to reload everything every time?

I created a video demo’ing the problem. I have 4 terminal applications I use regularly:

terminal.app                  # misbehaves
iterm2                        # misbehaves
webstorm terminal emulator    # misbehaves
vscode terminal emulator      # behaves!

VSCode actually behaves in the way I would desire. I am guessing that it does this by loading ~/.bash_profile in a parent shell, and only doing that once, when vscode boots up. All of the terminal windows in the app, are then subshells of that one parent shell.

Hopefully this video makes the issue clear:

This is a legit issue – most terminal applications do this whole thing wrong, however, quite miraculously, it appears that VSCode from Microsoft actually does this whole thing the right way, see this issue:


Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

I wonder it would be better to write a cliche in the sourced side, not sourcing side. (like C header files)

[[ "${_NAME_OF_THIS_LIBSCRIPT:-""}" == "yes" ]] && return 0

With this, you do not need to repeat the same boiler plate every time you source a script file, which should contain something meant to be shared across multiple places.
Or am I missing something?
To be really honest, I am not a bash programming master, so I really would like to have insights from others.

Solution 2

This morning I moved a bunch of personal information out of my bash_profile and bashrc and added them to a new file I call privaterc.

In my privaterc file I set this variable:


Now in my bash_profile I have added this line:

[[ $PRIVATERC_RUN != yes && -f ~/.privaterc ]] && source ~/.privaterc

This will cause privaterc to only be sourced if it hasn’t previously been sourced in this shell.

Also as for the profile weirdness you are seeing, the article I linked in the comment states the following about mac:

Mac OS X — an exception

An exception to the terminal window guidelines is Mac OS X’s Terminal.app, which runs a login shell by default for each new terminal window, calling .bash_profile instead of .bashrc. Other GUI terminal emulators may do the same, but most tend not to.

Solution 3

Put a unique variable in the rc file – with a length of 1 or more bytes – then check if it has a length greater than 0 before sourcing bash_profile.

if [ ! X”” = X”$uniq_var” ] ; then . ~/.bash_profile ; fi

I just set bash as the login shell on MacOS, and didn’t have this problem. Perhaps they changed something again.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply