Back

An Introduction to Dotfiles

Have you ever wondered what files like ~/.bash_profile and ~/.bashrc are good for? What do you do with them to begin with? Why are there so many of them? What’s the difference between them and which one do you use in which case? If so, this blog post is for you.

Getting Started

For starters, what does ~/.bash_profile even mean? Let’s dissect it. It’s simply a text file with the name .bash_profile. It doesn’t have (and doesn’t need) a filename extension, i.e., there’s no .txt or .html or something similar at the end. On macOS and Linux, when you prepend a file name with a dot, you make it a hidden file. That is, in order to view a hidden file in the macOS Finder, you have to press the three keys , , and . simultaneously in order to see such files. With the same key combination, you can hide them away again. Because their file name begins with a dot, such hidden files (which typically are configuration files) are commonly referred to as dotfiles. The tilde character ~ tells us that this file named .bash_profile lives in the home folder, e.g., /Users/davidculley/ on macOS or /home/davidculley/ on Linux. It’s an abbreviation that expands to your own user name. See for yourself and type this into the terminal:

echo ~

Why use an extra character to tell us that? Why not just say /Users/davidculley/.bash_profile and hard code your user name into the path? One reason is that your account is not named davidculley. If I wanted you to create a file, say, hello.txt on your desktop, I couldn’t simply tell you to execute the command touch ~/Desktop/hello.txt. You’d instead have to replace all references to my user name with your own if you want to follow my guide. Another reason, as you already know, is that Linux uses /home/ instead of /Users/. Scripts wouldn’t be very portable without abbreviations such as ~ and $HOME. Also, they make the path a lot shorter. So in summary, the following things all mean the same thing (where the last two options are the most portable):

  • /Users/davidculley/.bash_profile
  • /Users/$USER/.bash_profile
  • ~/.bash_profile
  • $HOME/.bash_profile

Into such a text file, you will write custom configurations, e.g., where Java lives on your system. After all, when you want to compile your *.java files with the compiler javac, macOS needs to know where it can find javac. Thus, if you want to develop Java code, you need to save the path to where you installed the Java SDK in the JAVA_HOME variable. Imagine a colleague telling you, “Come to my place tonight, I’m throwing a party.” without ever telling you where he lives. You could then tell the taxi driver, “Drive me to John’s place,” but how would you (or the taxi driver) know where to go? These dotfiles are the means by which you tell the system where applications actually live.

The names of these files follow a convention. You can’t rename them however you like. Your operating system expects them to have a certain name, e.g., .bash_profile or .zshrc, and depending on which case applies, the system looks into the according file. If you were to rename such a file to, e.g., .my-configuration-file, it’s as if that file wouldn’t exist.

Each shell has its own set of files

A shell is basically a terminal, or rather the program in the background that executes all the commands you type into the macOS app /Applications/Utilities/Terminal.app. There are different shells. A really old one is sh. You probably won’t encounter it anymore. Its successor is called bash, short for Bourne-again shell. It’s a funny word play because sh was developed by Stephen Bourne and thus is known as the Bourne shell. Bourne. Born. Get it? Bash is what macOS used up until macOS Catalina. In recent years, an alternative shell has become very popular because it is much better than Bash. It is simply called the Z shell or zsh. With macOS Catalina, Apple made this Z shell the default shell and thus replaced Bash for fresh installations of macOS. For existing user accounts, you have to change the shell yourself. Imagine the outcry if Apple would force such a change upon its users. I wrote a blog post of its own about how to switch from bash to zsh if you want to try this cool new shell with cool features like auto-complete for yourself.

Now, depending on which shell you use, you need to use different corresponding dotfiles. If you were using the shell sh, then you’d need to write your configurations into the file ~/.profile. But as I said, besides systems that haven’t been updated for a really long time, nobody really uses sh and ~/.profile anymore. If you use bash, which you probably do because bash has been the default shell on macOS for the longest time, then you need two different files: ~/.bash_profile and ~/.bashrc (more on when to use which of those file in a minute). And zsh of course has its own files too. Accordingly, they are called ~/.zprofile and ~/.zshrc. It even has a third file called ~/.zshenv (short for zsh environment).

The part “rc” derives from “run commands”. In the very early days (in the 1960s), developers stored startup information for a command in a file they named runcom (short for run commands). Basically, it contained all the commands that are to be executed when a program is opened. Every single time you open a new instance of bash, the system first executes the commands in ~/.bashrc. Every single time you open a new instance of zsh, the system first executes the commands in ~/.zshrc. The run commands in such files could be alias definitions and function definitions but also shell options and prompt settings. Similarly, there is a .condarc file for the Python package manager conda and a .vimrc file for the text editor Vim. If you want a deeper dive into the details of ~/.bashrc, this answer on StackOverflow was really helpful to me.

To understand the differentiation between these dotfiles better, let’s first look at what types of shells there are.

The Four Types of Shells

A shell can be either login or non-login. It can be further subdivided into interactive or non-interactive. Thus there are four possible combinations.

Since you most likely use Bash, I’m going to explain ~/.bash_profile and ~/.bashrc first. With regard to the bash shell, macOS’s behavior is a little different from Linux. While your operating system behaves differently regarding which of these two files it uses in which case, depending on whether you use macOS or Linux, this shouldn’t influence your behavior. The decision which file you should use when for storing your configurations shouldn’t be influenced by which operating system you use. Since macOS is the exception and deviates from the standard, I personally would do it the Linux way, even if you are using macOS. Stick to the standard. I myself am using macOS if you couldn’t tell by now, but I use my dotfiles as if I were using Linux. Not only because it is “the right way”, but because this way I can share my files among different machines, including Linux machines, and don’t have to do the configuration work again.

The Two Interactive Shells: How Linux Does It

As I said earlier, macOS operates differently from Linux. Since macOS is the exception and not the standard, I think it’s best to first understand how Linux does it and then look at what macOS does differently. Linux and macOS really only differ in two of the four cases … the two interactive cases:

  • interactive login shell
  • interactive non-login shell

So let’s examine these two cases. On Linux, an interactive login shell uses ~/.bash_profile. An interactive non-login shell uses ~/.bashrc. On Linux, the interactive login shell is loaded only once (when you log in somewhere, e.g., in to your user account davidculley—hence the name). The interactive non-login shell by contrast is loaded each and every time you open a new terminal window. I explicitly start my sentences with “on Linux” because—again—macOS behaves differently. Just because Linux does it this way doesn’t mean the same is true for macOS. Don’t let this confuse you when you read some answer on StackOverflow and they don’t mention which operating system they’re talking about.

On Linux, it would be like so: you boot your computer and log in to your user account. Since you just typed in a username and password combination, you logged in somewhere. At this point, everything in ~/.bash_profile is run. Right now, your computer has only loaded ~/.bash_profile but not ~/.bashrc. This is because you didn’t open the app Terminal.app (or rather, the bash shell), i.e., an interactive non-login shell. You only logged in to your user account. (This started some sort of graphical shell, in case you’re wondering.) Now, at this point it may be so that you want to open an application that needs to read one of those mentioned variables. For example, Matlab needs to know where Java lives on your system, i.e., it needs to read the JAVA_HOME variable. Remember, at this point you’ve only loaded ~/.bash_profile but not ~/.bashrc. This is the reason why we must set the PATH variable, the JAVA_HOME variable, etc. in ~/.bash_profile and not someplace else.

Also, loading such variables once (when you log in) usually is enough. After all, after a program was installed, it stays there. You typically don’t change its location anymore, at least not very often. And in the rare event that it actually does change it’s location or that you’ve needed to add a new variable after installing new software (after ~/.bash_profile has been loaded), e.g., the GOPATH variable for the language Go, you can simply re-load ~/.bash_profile by sourcing it, i.e., by executing the command source ~/.bash_profile. Therefore, things that don’t change very often, like PATH or JAVA_HOME or GOPATH, belong into ~/.bash_profile. You don’t need to tell Linux where you’ve installed Java every single time you open a new terminal window when it’s so easy to re-load it only once it became necessary.

Putting the PATH variable into ~/.bashrc would do exactly that. On Linux, each and every time you open a new terminal window, Linux reads the file ~/.bashrc. This is why you put your aliases, your function definitions, and everything else that concerns bash itself in ~/.bashrc. Whenever a new instance of bash starts, it should have available the latest version of its settings (remember what we said earlier about run commands). Basically, ~/.bashrc contains everything that Linux should refresh every single time you open a new terminal window. Think of it this way: after you’ve installed all your most used software, it is a lot more common that you add or change an alias than that you install an additional program. When you open a new terminal window, you always want to have your latest alias and function definitions, not just those you defined prior to your last login. Also, you don’t want to have to remember to always source your dotfiles every single time you change your aliases.

Another point: a “rc” file is meant to contain the run commands for a given program, and only those commands. That is, .bashrc is meant to contain (only) the run commands for bash. In other words, everything that has to do with bash itself, and only those things, should go into ~/.bashrc. Java is not Bash. Therefore, telling the system where Java lives has nothing to do with bash itself and therefore JAVA_HOME not belong into ~/.bashrc.

On the risk of repeating myself: if you didn’t enter a username and passwort combination somewhere, you did not log in anywhere. When you open a new terminal window, all you did was open a window. No login, however. Therefore, on Linux, a new terminal window will be an interactive non-login shell.

To summarize what we’ve looked at so far: we’ve examined two of the four cases, the two interactive ones, specifically for the shell bash. It’s different for other shells, e.g., zsh. We’ve specifically examined how Linux does it. Apple’s macOS does it differently. We will examine how macOS does it in a moment.

From the Bash Reference Manual:

When bash is invoked as an interactive login shell, […] 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.

On Linux, an interactive login shell is what you get when you log in to your Linux user account, e.g., davidculley. Then, bash will read ~/.bash_profile if it exists. In case there is no ~/.bash_profile, it will look for the file ~/.profile as a fallback. However, if bash found ~/.bash_profile, it will stop looking further and will not look into ~/.profile.

In former days, ~/.bash_profile was meant to differentiate between the bash shell and its predecessor sh. But since sh isn’t used very much anymore and bash, as a successor of sh, is 100% backwards-compatible with sh, it doesn’t really matter whether you use ~/.bash_profile or ~/.profile for the bash shell. Just know that ~/.profile will not be read if there is a file called ~/.bash_profile.

This differentiation is mostly a concern if you’re working on multiple machines, e.g., a Mac at home and Linux at work, and one of these Linux machines is still using the older sh instead of bash, but you nevertheless want to share your dotfiles with your work machine. If you have that kind of problem, you can put your environment variable definitions, e.g., PATH, JAVA_HOME or GOPATH, into ~/.profile and source this ~/.profile file from within the ~/.bash_profile file.

Quoting the Bash Reference Manual again:

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists.

Only interactive non-login shells read the file ~/.bashrc—and only ~/.bashrc, at that.1 On Linux, every regular new terminal window is an interactive non-login shell.

Maybe you’re wondering, “If interactive login shells load only ~/.bash_profile and interactive non-login shells load only ~/.bashrc, then how can it be that I can nevertheless open a terminal window and compile some Java files using the Java compiler javac in a interactive non-login shell, even though I specified JAVA_HOME in ~/.bash_profile? I thought interactive non-login shells do not load ~/.bash_profile?!” You’re right. You opened an interactive non-login shell and it did not read ~/.bash_profile. However, interactive non-login shells inherit everything the interactive login shell knows. On Linux, this means that every terminal window started during a login session automatically inherits what was loaded from ~/.bash_profile when you logged into your user account, e.g., davidculley. That’s why you don’t have to set environment variables such as the PATH or JAVA_HOME variable in ~/.bashrc as well.

Consider the following example: you boot your computer and log in to your user account davidculley. This starts an interactive login shell. This shell loads the file ~/.bash_profile and everything it contains at this exact moment. Five hours later, you open a new terminal window, i.e., an interactive non-login shell. This new terminal window inherits all the information from the interactive login shell, e.g., the content of the PATH variable, although it’s five hours old already. The interactive non-login shell (i.e., the terminal window) won’t know the latest information from ~/.bash_profile. After all, you could have changed ~/.bash_profile in the last five hours. It will, however, have the latest information from ~/.bashrc, because the terminal window just re-loaded ~/.bashrc when it was started. The information from ~/.bash_profile is only inherited but not re-loaded.

The Remaining Two Types

Before we look at how macOS does it and how it differs from Linux, let’s first get the remaining two types of shells out of the way.

A non-interactive non-login shell is what’s started when you execute a regular old script. For example, if you have a script named my-script.sh sitting on your desktop and you enter the following commands into your terminal, then you start a non-interactive non-login shell:

cd ~/Desktop
./my-script.sh

Non-interactive shells that are also login shells are rare, very specific and very advanced, so don’t worry about them. If you don’t know what a X display manager is and if you also don’t pass local scripts over an SSH connection to remote hosts, then just forget that non-interactive login shells exist. This eliminates one of the four possible cases entirely. So let’s get back to the two interactive types and see what macOS does differently from Linux.

The Two Interactive Shells: How macOS is different from Linux

What Linux does, makes sense. When you log in to your user account, an interactive login shell is started, i.e., ~/.bash_profile is read. Once. After you’ve logged in, there’s no need to log in yet another time. You’re already logged in. Therefore, after your initial login, opening a new terminal window will start an interactive non-login shell, i.e., every time you open a new terminal window, ~/.bashrc is read.

In contrast, whenever you open a new terminal window on macOS, macOS opens an interactive login shell. That is, whenever you open a new terminal window on macOS, macOS looks into ~/.bash_profile, not into ~/.bashrc as Linux does. Even though you didn’t log in anywhere. All you did was open a window. You didn’t enter your username and password anywhere. That macOS starts new terminal windows as login shell rather than as non-login shell means two things for macOS users:

  • Per convention, things that don’t change very often, e.g., the PATH or JAVA_HOME variable should go into ~/.bash_profile. This ~/.bash_profile does not need to be reloaded very often, because its content rarely changes. It’s sufficient to reload it only when you log in to your user account and, if need be, manually via source. Yet macOS reloads it every single time you open a new terminal window.
  • Per convention, you would place your aliases and everything else you want to be reloaded with every new terminal window into ~/.bashrc. But when you open a new terminal, macOS does not look into ~/.bashrc. It instead looks into ~/.bash_profile.

The first point is an annoyance, but the second point is a problem. Since ~/.bashrc is not read when you open a new terminal window, your aliases and function definitions won’t work if you stick to the standard and put them where they belong, in ~/.bashrc. On macOS, you therefore need to source ~/.bashrc from within ~/.bash_profile as described in a following section.

To recapitulate, whenever we open a new terminal window

  • Linux starts an interactive non-login shell
  • macOS starts an interactive login shell

What does this mean with regard to our dotfiles setup? The consequences are the following: if you use bash and one of these cases applies:

  • you want to stay portable, e.g.
    • you own both a Mac and a Linux computer and you want them to share the same dotfiles
    • you want to make your dotfiles publicly available on GitHub
  • you simply want to stick to the convention and put your aliases where they belong (in ~/.bashrc)

then I recommend you do it as Linux would expect it, even if you use macOS.

Maybe you’re wondering which way is better—the way Linux does it or the way macOS does it? I recommend you read through the comments of this StackOverflow answer. To paraphrase: in most cases, there is no harm in the way macOS does it. But if you want to set up different variables in a terminal, the macOS way would override environment variables provided by the environment, i.e., it would override your local settings. This can be frustrating. The advantage of the macOS way is that after you’ve, e.g., updated your PATH variable in ~/.bash_profile, you can simply close and re-open your terminal window. On macOS, there’s no need to explicitly source ~/.bash_profile for the changes to take effect.

So if macOS starts interactive login shells for new terminal windows, i.e., almost exclusively uses ~/.bash_profile, then what is the job of ~/.bashrc on macOS?

Sometimes you will also use bash to start a new instance of bash, i.e., you will start a new shell session within your existing shell session. If you’re familiar with Homebrew and/or you’ve read my blog post on installing software on macOS via Homebrew, then you will recognize the following command:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

We used bash to start another instance of bash. Since you never entered any user credentials, this shell within a shell will be an interactive non-login shell, i.e., it will load the file ~/.bashrc. It will not re-load ~/.bash_profile, since only interactive login shells load that file. Remember the above-mentioned quote:

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists.

On macOS, only such shells within another shell (“second-instance shells”) are interactive non-login shells. Normally, regular “first-instance shells” would be interactive non-login shells as well, just like on Linux. But macOS treats “first-instance shells” differently than Linux, as we already know. “Second-instance shells”, however, work the same on macOS and Linux. They are interactive non-login on both operating systems and thus read (only) ~/.bashrc … on both operating systems. Such shells within a shell load neither ~/.bash_profile nor ~/.profile. However, they do inherit what is defined in ~/.bash_profile or ~/.profile. They inherit from the interactive login shell that started them. Therefore, what’s defined in ~/.bash_profile is used by the first-instance bash shell and the second-instance bash shell. On macOS, unless you source ~/.bashrc from within ~/.bash_profile as described later, the content in ~/.bashrc will be applied only to second-instance shells. On macOS, you therefore have two options:

  • deviate from the standard (not recommended) and put your aliases and environment variable definitions into ~/.bash_profile
  • source ~/.bashrc from within ~/.bash_profile (recommended)

If you follow neither of these options, i.e., put your aliases into ~/.bashrc but don’t source this file, your aliases will not work because then only second-instance shells would know of these definitions. Variables such as PATH or JAVA_HOME would then be set only in programs that were launched via the terminal. In case you open them via the Finder or Spotlight, these variables wouldn’t be set. As a consequence, if you use Matlab or other programs that need to know where they can find Java, e.g., build tools like Maven or Gradle, they wouldn’t find Java and would tell you Java wasn’t installed, even though it is, simply because they don’t know of the JAVA_HOME definition.

Does the first option mean that you can simply put everything into ~/.bash_profile? You could, yes, but then you would go against the convention. The convention says to define aliases in ~/.bashrc because on Linux that file is reloaded very frequently, much more frequently than ~/.bash_profile. And you would put stuff into ~/.bashrc that is specific to second-instance shells, i.e., interactive non-login shells.

Consider Linux again. On Linux, ~/.bashrc is read

  • whenever you open a new terminal window or
  • whenever you start a new shell session from within an existing shell session, e.g., when you use bash to call another instance of bash.

The first case works differently on macOS, as we’ve already seen. And because of that different behavior of macOS, the only circumstance that ~/.bashrc will be read on macOS is this second case. For example, when we type /bin/bash into the terminal to install Homebrew.

Zsh Specifics

To make it even more complicated, zsh behaves a bit different than bash. With zsh, it is not either ~/.bash_profile or ~/.bashrc like with Bash but first ~/.zprofile then ~/.zshrc, i.e., both files are read. With all we’ve learned so far, this seems to be an extra thing we have to pay attention to, but actually it makes things a lot easier. Remember all the differences between Linux and macOS? They exist because bash only reads either the one file or the other, depending on whether it’s a login or non-login shell. Bash does not read both files and Linux and macOS are in disagreement with one another whether a new terminal window should be a non-login or a login shell. If you use zsh, then all of this is irrelevant because zsh does read both files. With zsh, it doesn’t matter whether a new terminal is an interactive login shell or an interactive non-login shell. The Z shell will read both ~/.zprofile as well as ~/.zshrc, in either case.

The file ~/.zshrc is read in two of four cases (the exception being the rare case and the very common case of running local scripts). If you want to cover all four cases, then you can put everything in ~/.zshenv. But sometimes you don’t want your custom settings to interfere with your scripts. Such highly custom settings would go into ~/.zprofile or ~/.zshrc, depending on whether you want to cover the rare case or not. If you don’t know what a X display manager is and also don’t pass local scripts over an SSH connection to remote hosts (the rare case), then you don’t need ~/.zprofile.

Comparison of All Four Types

What really clarified my understanding was the following image I found here. The color coding is:

  • interactive login → red
    • Linux: read once, when you log in to your user account
    • macOS: read when you log in to your user account and every time you open a regular terminal window
  • interactive non-login → green
    • Linux: read every time you open a regular terminal window and the shell within a shell
    • macOS: the shell within a shell
  • non-interactive login → yellow
    • the rare one
  • non-interactive non-login → blue
    • whenever you execute a local script

Shell startup scripts
Shell startup scripts

As you can see, bash (on the left) is a mess and things got a lot simpler with zsh (on the right).

You can find a really good explanation of the four different kinds of shells here.

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.

Since Bash is the default shell, I will describe the process for Bash.

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 ~/.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 for a simpler migration to a different shell, should I decide to switch to a different shell, say zsh or fish, or back to bash. 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:

nano ~/.bash_profile

To exit first press Ctrl+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 or Visual Studio Code installed, you could also open a file in Sublime as follows:

subl ~/.bash_profile

To open the file in VS Code, you’d first need to add Visual Studio Code to your PATH so that macOS knows what you mean by the word code.

echo 'export PATH="$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin"' >> ~/.bash_profile
source ~/.bash_profile

Only now can you use VS Code to open ~/.bash_profile:

code ~/.bash_profile

After you’ve opened ~/.bash_profile, add the following lines to the existing content:

if [ -r ~/.profile ]; then . ~/.profile; fi
# If the shell is interactive, source ~/.bashrc
case "$-" in *i*) if [ -r ~/.bashrc ]; then . ~/.bashrc; fi;; esac

This loads the content of ~/.profile and ~/.bashrc into ~/.bash_profile so that you don’t have to write things twice. This is what’s commonly referred to as “sourcing”. Open ~/.bashrc in the same way and replace its content with:

source ~/.aliases

Since you sourced your aliases into ~/.bashrc where they belong according to convention, and your ~/.bashrc into ~/.bash_profile, you don’t need to load your aliases into ~/.bash_profile anymore.

Then, open ~/.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"

I’m sure you’ll find a lot more useful aliases if you do a quick Google search.

Now back to ~/.bash_profile. Open it and add your environment variables, i.e., the lines of code that look like this:

# Homebrew
export PATH="/usr/local/bin:/usr/local/sbin:$PATH"
# Java
export JAVA_HOME=$(/usr/libexec/java_home)

Of course these are not my complete dotfiles. Everybody has different preferences and uses different aliases, thus a Google search of 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, if you switch your shell from bash to zsh you also have to change your current configuration. Otherwise things will break and applications will tell you that Java isn’t installed even though it is. The Z shell reads neither ~/.bash_profile nor ~/.profile and will not know of any definitions stored in these files. bash is designed to read the same syntax as sh, i.e., bash is backwards-compatible with sh and can read both ~/.bash_profile and ~/.profile. In contrast, zsh was designed with a different syntax in mind. That is, zsh is not 100% compatible with the syntax of Bourne-like shells, i.e., bash or sh. Therefore, the Z shell uses its own files and I don’t recommend you simply source ~/.bash_profile into these zsh files as we did earlier.

In case you’d nevertheless like to source ~/.bash_profile into ~/.zshrc, you should at least tell zsh to emulate sh to read sh syntax. It’s no guarantee that everything will work, but it helps. To do so, open ~/.zshrc like we did before:

open -e ~/.zshrc

Then, replace its content with:

[[ -e ~/.bash_profile ]] && emulate sh -c 'source ~/.bash_profile'

All other settings regarding zsh, such as aliases, go into ~/.zshrc as well. That’s why we created a separate file for our aliases and didn’t put them directly into ~/.bashrc. This way we’re now able to simply source them from ~/.zshrc as well:

source ~/.aliases

That’s it! I hope everything is working on your machine.

Finals Words

If you have any questions, please don’t hesitate to ask in the comment section down below. If this article was helpful, why don’t you let me know? 😊 Nice comments from readers like you are what keeps me motivated to write guides like this for you. Also, if you spot any errors, I’d appreciate your comment. If you want to get notified when I publish a new article like this, consider following me on Twitter. Thanks, and be well.


  1. That’s an oversimplification. In order to not confuse you, I purposefully omit the files higher up the hierarchy located at /etc/. Right now, you don’t need to know about them. ↩︎

Last updated on Jan 27, 2020
As an Amazon Associate I earn from qualifying purchases.
Built with Hugo
Theme Stack designed by Jimmy