Have you ever asked yourself what the
~/.bashrc files are good for? What do you do with them in the first place? Why are there so many of such files? And which one do you use in which case? If so, this blog post is for you.
From the Bash Reference Manual:
When bash is invoked as an interactive login shell, […] it looks for
~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.
When an interactive shell that is not a login shell is started, bash reads and executes commands from
~/.bashrc, if that file exists.
What does that mean for macOS users?
On macOS, opening a new terminal window invokes a login shell by default. A shell can be login or non-login as well as interactive or non-interactive. So there are four possible combinations. Non-interactive login shell are very rare, so don’t worry about them. When we talk about login shells on macOS, let’s agree we’re talking about interactive login shell. That being said, whenever you open a new terminal windows
bash will try to read
~/.bash_profile before it reads anything else, according to the
bash reference manual. Only in case
~/.bash_profile does not exist,
bash will try to read
~/.profile as a fallback.
In former days,
~/.bash_profile was meant for the
bash shell and
~/.profile was for its predecessor, the
sh shell. But since
sh isn’t used very much on macOS anymore, it doesn’t matter whether you use
~/.profile for the
bash shell. This is mostly a concern if you’re working on multiple machines, e.g. a Mac at home and Linux at work, and you want your dotfiles to also work on machines where your login shell isn’t
If you have that kind of problem, you can put your environment variable definitions like
~/.profile and source them from
~/.bash_profile. If you only have a Mac and that is your only computer, it doesn’t matter whether you choose
~/.profile. Just pick one of them and stick with it, since
~/.profile will not be read if
In case you start to use
bash inside of existing sessions of
bash, you get an interactive non-login shell. You can find a really good explanation of the four different kinds of shells here. Only such interactive non-login shells read
~/.bashrc. But these “second-instance shells” read neither
~/.profile, they read only
~/.bashrc is the place where you put stuff that isn’t inherited by subshells automatically and applies only to
bash itself, such as alias and function definitions, shell options, and prompt settings. Don’t put environment variable definitions (e.g. as
~/.bashrc, since they will then only be set in programs launched via the terminal, not in programs started directly with an icon or menu or keyboard shortcut. For example, you need to set
JAVA_HOME for build tools like Maven or Gradle, and I think Matlab needs
JAVA_HOME, too. If you put
~/.bashrc, I think these programs wouldn’t know where Java lives on your machine and therefore wouldn’t work. If you want to read more, this answer on StackOverflow was really helpful to me.
How to edit these files
Now that we have a reasonable understanding of these files and what goes where, you can choose to either create new files from scratch or migrate your existing configuration to the more appropriate files. Be sure to backup you configuration so that you don’t lose data and your applications aren’t working anymore afterward.
Enter the following commands into a terminal to create the relevant files if they don’t exist yet. In case they already exist, these commands will do no harm and only update the “Last modified on” date:
touch ~/.bash_profile touch ~/.profile touch ~/.bashrc touch ~/.aliases
I decided to put my aliases in a separate file called
.aliases rather than in
~/.bashrc and source it from
~/.bashrc instead. This allows me to keep my aliases, should I decide to switch to a different shell, say
fish. More on this later.
Since these files are hidden, you won’t be able to see them in Finder by default. There are several options to open them. In case you’re familiar with the text editor called
nano, you can open and edit a file like so:
To exit first press
X and then
Y to save.
In case you’re not comfortable with
nano, you can edit a file with Apple’s TextEdit like so:
open -e ~/.bash_profile
In case you have Sublime Text installed, you could also open a file in Sublime as follows:
After you’ve opened
~/.bash_profile, replace the existing content with the following lines, so that
~/.bash_profile contains nothing else than this:
if [ -f ~/.profile ] ; then . ~/.profile fi # If the shell is interactive, source ~/.bashrc case "$-" in *i*) if [ -r ~/.bashrc ]; then . ~/.bashrc; fi;; esac
~/.bashrc the same way and replace its content with:
~/.aliases and enter your shortcuts for frequently used terminal commands. A few examples could be:
alias ..="cd .." alias ...="cd ../.." alias softwareupdate='softwareupdate -i -a' alias bu="brew update; brew upgrade; brew cleanup; brew doctor" alias cu="conda update --all --yes; conda clean --all --yes --quiet" alias pu="pip3 list --outdated --format=columns"
I’m sure you’ll find a lot more if you do a quick Google search.
The last file missing is
~/.profile. Open it and add your environment variables, the lines of code that look like this:
# Homebrew export PATH=/usr/local/bin:$PATH # Java export JAVA_HOME=$(/usr/libexec/java_home) # Ruby if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi # Go export GOPATH=$HOME/go export PATH=$PATH:$(go env GOPATH)/bin
Of course these are not my complete dotfiles. Everybody has different preferences and uses different aliases, so a Google search how other developers are organizing their dotfiles is worthwhile. I hope, however, I could set you on your feet with this basic introduction, so that you can build your own dotfiles.
What if I’m using the Z shell?
If you followed my guide on how to customize your terminal, you are probably using the much more powerful and customisable Z shell instead of the default shell
bash. The Z shell offers many features
bash is lacking, like auto-completion or support for plugins such as syntax-highlighting. It also let’s you change prompts which is especially handy when working with git.
However, by switching your shell from
zsh you have to change your current configuration. The Z shell does not read
~/.profile, since its syntax is not completely compatible with the
sh shell or
~/.profile are designed for Bourne-like shells, thus
sh but not
zsh. The Z shell uses its own file,
zsh works a little bit different than
bash which reads either
~/.bashrc depending on whether
bash runs as login shell or non-login shell,
zsh doesn’t make that difference. The Z shell reads both
~/.zshrc, in that order, no matter whether an interactive shell is a login or non-login shell.
So if you want to re-use your current configuration and also be able to switch between
zsh without losing your configuration or the need to write everything twice, you can source
~/.zprofile, so that the environment variables in
~/.profile work for
zsh. First, create
As I said, the syntax of
zsh is not completely compatible with
bash. To read
sh syntax with
zsh, we need to emulate
sh. To do so, open
~/.zprofile like we did before:
open -e ~/.zprofile
Then, replace its content with:
[[ -e ~/.profile ]] && emulate sh -c 'source ~/.profile’
All other settings regarding
zsh, such as aliases, go into
~/.zshrc. That’s why we created a separate file for our aliases and didn’t put them into
~/.bashrc. This way we’re able to source them from
That’s it! I hope you’ve got everything to work on your machine. If you have any questions, please don’t hesitate to ask in the comment section down below.