tldr
On macOS: shells are interactive
, login
which causes ONLY bash_profile
to get run. This causes bashrc (and shrc) to never get run on macOS. This problem does not occur for zsh or bash on other *nix. (update: this is an issue for zsh as well on macOS)
In your current setup, the bashrc (and thus shrc) files never get run when on macOS (fresh install).
Define behavior of macOS for bash
macOS runs each shell as a login
shell.
Here are some additional details: http://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default
Define behavior of login
shells in bash (only bash_profile is run)
Since macOS runs each shell as a login
shell, it only runs the bash_profile
and ignores the bashrc
file.
From the SO:
Now, a kind of a silly peculiarity of bash, not shared by most other shells, is that it will not automatically run .bashrc if it's started as a login shell.
From man bash
:
shows that bash_profile
gets executed for login
shells
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if
that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that
exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.
shows that bashrc
is NOT executed for login
shells
When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists.
thus, bash_profile
is the only thing executed for login
shells
Potential Solutions
Solution 1: Source things differently on macOS
One solution is to source the bashrc
from the bash_profile
only on macOS AND source the other way around only on NOT macOS.
# .bash_profile
[ $(uname -s) = "Darwin" ] && source ~/.bashrc
.bashrc
[ $(uname -s) != "Darwin" ] && source ~/.bash_profile
Issue with this solution: running bash in a subshell will now only execute bashrc
and not bash_profile
. You need to execute bash --login
instead and it will also run it in the opposite order (profile first, bashrc second) so that's something to keep in mind.
Solution 2: Change the command used to start the shell
Document that you need to start bash in interactive
, non-login
mode (the standard linux way).
- The command would be:
bash
or to be explicit bash --noprofile -i
- In iTerm2, you can do this like so and everything works great:
- Unfortunately this doesn't work for Terminal.app out of the box.
- You can either set the global login shell
- OR specify the shell/command to be used in Terminal profiles.
- Unless you use "Run inside shell", all other options will prepend a "-" before the command causing your bash to run in login mode which means bash_profile gets run and bashrc does not.
Would love your thoughts on a portable solution. Thanks!
Addendum
Some additional details to verify everything I said above.
Checking that the shell is Interactive
You can test that the shell is interactive by running echo $-
. If "i" exists within the output, the shell is interactive.
From man bash
:
PS1 is set and $- includes i if bash is interactive, allowing a shell script or a startup file to test this state.
Checking that the shell is login
You can test this by running echo $0
after starting the shell. If it returns "-bash" then it has started as a login shell.
From man bash
:
A login shell is one whose first character of argument zero is a -, or one started with the --login option.
Last login: Wed Mar 22 15:27:20 on ttys001
loaded bash_profile
Als-MacBook-Air ~ # echo $0
-bash
Als-MacBook-Air ~ # bash
loaded bashrc
loaded bash_profile
Als-MacBook-Air ~ # echo $0
bash
Als-MacBook-Air ~ #
You can see in the example above, it only loads the profile and not bashrc as it has started as a login
shell. After running bash
as a subprocess, it loads both the bashrc and the profile and it is a non-login
shell.
Your assumption right now is the opposite (which I believe applies in the Linux world), that each shell is interactive
and non-login
so bashrc
will run first and you source the bash_profile
from within that script.