Confused by profiles and bashrc? Read on!
I love Linux, and if you're reading this, chances are you do too. To be honest though, some aspects of the Linux environment are confusing. Near the top of the list for me is the profile system. Conceptually, it's simple. There are system-wide settings that all users inherit, and then there are individual settings people can set on their own. The problem comes when different distributions handle profiles in different ways, and the concept of login shells versus interactive shells comes into play. Usually, it's not something Linux users worry about. But, when you need to make a change, it can be extremely frustrating to figure out what is loaded in what order, and which is seen by login shells only, and so on.
First, let me clarify what I mean by login shells. You've probably noticed that sometimes in order to get to a terminal shell, you're prompted for a user name and password. Other times, you just click on the terminal icon, and you're presented with a terminal already logged in. You'll most often experience this when using a GUI desktop environment. Basically, if you're already logged in to your Linux desktop, and you open a terminal window, it's an interactive shell.
It doesn't have to be inside a graphical desktop environment, however. If you ssh in to a remote server, you're prompted for a user name and password (thus, a login shell). If you then type bash from inside that SSH session, you're starting a brand-new terminal, but this time, it's an interactive shell (notice you're not prompted for a password). Why it matters is something I'll talk about a little later, but for comprehension sake, just remember that if you're prompted for a user name and password, it's most likely a login shell. If you go directly to a bash prompt, it's most likely an interactive shell. The one fairly common exception to this is if you've set up SSH keys to log in automatically. In that case, even though you aren't prompted for a user name and password, it's still a login shell. It's a pretty safe bet that if you're using SSH to log in, it's a login shell.
The login shell process is far more complicated than interactive shells, so I am going to go over that process first. I'm assuming your users have a bash shell assigned in their /etc/passwd files. It's the most common shell for users to have, so it makes sense to be familiar with its nuances.
Step 1: when you authenticate in to a login shell, the system looks for a file called /etc/profile. That file is a shell script that assigns a few environment variables all users should have set.
Step 2: the /etc/profile script usually ends by calling any shell scripts in the /etc/profile.d folder and executing them as well. Often it will run only shell scripts in /etc/profile.d that end with a .sh extension, so look at the /etc/profile script to see how files should be formatted to run properly. Having a folder to add custom scripts is important, because if you have system-wide changes you'd like added to everyone's login shell, adding commands to the /etc/profile file is dangerous. Any system updates affecting /etc/profile will overwrite your changes. If you simply add a custom file into the /etc/profile.d folder, it will be read by the updated /etc/profile script even if it's updated.
Step 3: the /etc/profile script also executes the user's personal profile. This part is a little messy, as the user profile might be called different things depending on distribution and/or user customization. In general, the system will try loading the profile by name in this order:
If it finds a file with that name in the user's home directory, it executes it and stops. This means if you have a .bash_profile and .profile in your home directory, only the .bash_profile will be executed. This is useful to know if you want to customize your profile, but don't want to make changes to the original user profile assigned to you. By default in Ubuntu, every user has a .profile file, but not .bash_profile or .bash_login. So if you want to customize your profile, simply copy the .profile in your home directory to a file called .bash_profile, and make any changes you want to .bash_profile. Doing that will leave your original .profile intact and still will allow you to customize to your heart's content. Just remember, if you create an empty .bash_profile, the system will see that as your profile of choice and ignore your .profile file completely!
Step 4: finally, the last step along the login shell order of operations is the .bashrc file stored in the user directory. This is another script—this one called from the .profile script in Step 3. Note that if you customize your user profile settings, you'll want to make sure whatever profile file you use actually calls the .bashrc script. It's inside the .bashrc script where personal settings like a custom prompt and color settings go, along with command aliases you might want to set (more on those later).
Step 5: this step doesn't really take place after Step 4; rather, it sort of branches off at Step 1. The /etc/profile script starts the process for loading user profiles, but it also kicks off the process for executing the system-wide bashrc file. Here again various distributions name this file differently, but it's generally either a file called /etc/bashrc or /etc/bash.bashrc. In the case of Ubuntu, it's /etc/bash.bashrc, but historically, it's often /etc/bashrc. Note that unlike the user's .bashrc file, the system-wide bashrc file does not start with a period.
To add insult to injury, some systems don't actually execute the system-wide bashrc file for login shells, so if you don't see it called in the /etc/profile script, that means it's not going to execute for login shells. For the most part, however, the /etc/profile on the majority of distributions does indeed call the system-wide bashrc file. Since you know the order with which profiles are loaded, you can investigate on your own system to see what is actually loaded during the login shell startup.
An interactive shell has a far simpler startup procedure. When a person opens an interactive shell (one that doesn't authenticate a user name or password), the following steps occur.
Step 1: the /etc/bashrc or /etc/bash.bashrc file is executed. This takes place whether or not it's referenced in /etc/profile. While a login shell automatically starts the /etc/profile script, an interactive shell automatically starts the /etc/bashrc (or /etc/bash.bashrc) script.
Step 2: the users' .bashrc from their home directory is executed. Again, like the system-wide bashrc file, this isn't called from a user profile; rather, it's executed directly by the interactive shell. So even if you've erased the reference to .bashrc from the .profile script, an interactive shell still will execute it.
And, that's it! An interactive shell doesn't look for any profile information at all, either system-wide or in the user directory. However, because an interactive shell is a “child” process of the login shell used to log in initially (either via GUI or SSH), it inherits all the profile information that initial login shell acquired. So although both the initial login shell and the “child” interactive shell have the same profile information loaded, the important distinction is that interactive shells don't reload the profile information. They never look at the profile scripts, so whatever information was loaded by that initial login script is all they have access to. (This distinction will be more important when you see what the scripts actually do.)
First, a disclaimer: I can spell out only what is generally done with profiles and bashrc scripts. It's certainly possible for a person to change what is done by customizing either profiles or bashrc scripts. Generally, it's good practice to stick to the standards.
Profiles mainly are used to load environment variables. Since profiles are loaded by login shells, and login shells are the initial entry point into a system, that's the time when setting up the environment makes the most sense. One of the biggest environment variables is the PATH variable. When a login shell is initiated, the PATH is set. Other environment variables also can be set in the system-wide profile or individual user profiles, but just know that the profile system is where most variables are set.
The order with which profile information is loaded is very important, because if you want to override the system-wide default profile information, you can do so by specifying environment variables in your personal user profile script. For instance, the PATH variable is usually modified by the user's profile script on login. Usually, the .profile (or .bash_profile, etc., see above) script will add ~/bin to the PATH variable if users have their own bin folder inside their home directory. Because user profiles are loaded after the system-wide profile, user settings take precedent and override system-wide settings.
Again, this is a generalization, but the system-wide bashrc file and then the individual user's .bashrc script usually set personal preferences for the command line. If you want a custom prompt, or prefer a specific color scheme, the bashrc system is where that would be set. Much like the profile system, the user's .bashrc file overrides the system-wide bashrc (or bash.bashrc, again see above) settings. That means you can customize the behavior of the command line however you like without affecting other users on the system.
The most common customization inside the .bashrc file is to add aliases. An alias is sort of like text expansion, in that it substitutes your defined alias with whatever command you specify. For example, here's a snippet from a .bashrc file in the user's folder:
alias ll='ls -alF' alias la='ls -A' alias l='ls -CF'
The aliases make it so that if the user types ll on the command line, the system will execute ls -alF instead. It's a great way to make shortcuts for commands with cryptic options or shortcuts for commands you type often.
Although I'm not suggesting tomfoolery, .bashrc aliases are also a great way to prank your fellow users if they leave their system logged in. Say you create an alias like this:
alias ls='echo "Deleting all files..."'
Then, every time they type ls, they'll be in for a little (innocent) surprise! Yes, it's very easy to do nefarious pranks with aliases, but since we all log out when we leave our workstation, we shouldn't ever have to worry about it, right?!
Understanding how shells work really makes troubleshooting a lot easier. You've probably already realized a few things, but they're worth mentioning. If you make changes to any of the profile scripts, those changes won't be recognized until you start a new login shell. The same is true for .bashrc changes, but since you easily can close an interactive shell and start a new one, .bashrc changes are easier to activate.
One of the main problems regarding profile loading is that if you make a change to environment variable settings, like the PATH variable, you'll actually have to log out and log back in to test your changes. You certainly can set a path variable on an interactive shell, but remember, any new interactive shells will inherit the original login shell's profile settings, so often logging out and back in is really the only way to make permanent changes.
Also, although I went over it already, I want to reiterate that while the system-wide profile (/etc/profile) and the user profile generally call the bashrc scripts, they don't have to. If you make changes to your profile settings, it's possible that your login shells will behave very differently from your interactive shells. If that's your goal, great, but usually you want to make sure your login shells also execute the bashrc stuff, since that information is what makes the user experience more useful.
Finally, I want to add that the best way to understand and learn about profiles and RC files is to play with your system. Learning how to manipulate your settings is not only educational, but it can make your computing experience much more convenient.