38

I'm having trouble wrapping my head around what belongs in ~/.profile and what belongs in ~/.bashrc.

From what I've read, it seems to me that ~/.profile should be used for environment variables and ~/.bashrc for aliases, functions, and the like. If I move all my export statements from ~/.bashrc to ~/.profile, would everything work as it should, or am I about to break something?

(Here's my dotfiles repo if you want to look over both files.)

EDIT 2022-06-03: I've moved most of my environment variables from ~/.bashrc to ~/.profile and have assured myself that ~/.bash_profile sources ~/.profile. The only environment variables I've left in ~/.bashrc are those that only matter when I'm working in a shell, e.g., those pertaining to less, the prompts, slrn, and the like. Everything seems to be working well. Thanks for the help.

MDeBusk
  • 1,175
  • 2
  • 8
  • 21

3 Answers3

40

It helps to understand which files get sourced when and why.

.profile is sourced by a login shell on startup. Typically, the only login shell you start is the one started when you log in, but you can run a login shell at any time with bash -l. (Also, on macOS, there is no initial login shell, so terminal emulators tend to run a login shell for each new window.)

.profile is an ideal place to set environment variables that can be inherited by any program started from the login shell.

.bashrc, on the other hand, is sourced by non-login interactive shells, such as those started by terminal windows. This is where you set things specific to your interactive shell that aren't otherwise inherited from the parent process. For example, PS1 is set here because only interactive shells care about its value, and any interactive shell will source .bashrc anyway, so there is no need to define and export PS1 from .profile.

And though you didn't ask, it's worth pointing out the difference between .profile and .bash_profile here. .profile is "shared" by all POSIX shells (such as dash), so don't put anything bash-specific here. .bash_profile, though, is only used by bash, so you can use bash extensions in it. If .bash_profile is present, .profile will be ignored, so if for whatever reason you want to use both, you can add . ~/.profile to the top of your .bash_profile.

MDeBusk
  • 1,175
  • 2
  • 8
  • 21
chepner
  • 676
  • This is helpful. Thank you. And I do appreciate the last paragraph. I don't think I have anything bash-specific regarding environment variables, and I do have both ~/.profile and .~/.bash_profile Looks like I have some more poking around to do. – MDeBusk Jun 02 '22 at 14:52
  • 1
    Mostly, it just means avoiding using things like [[ ... ]] in .profile. – chepner Jun 02 '22 at 15:33
  • No, it's not run for every command you issue in a terminal. It's sourced by bash when it is started as an interactive shell (i.e., when its standard input is a terminal or the -i option is explicitly used). – chepner Jun 02 '22 at 15:58
  • 1
    @MDeBusk If you look at the top of the ~/.profile file, you will see that ~/.profile is not called by bash if you have a ~/.bash_profile. – Terrance Jun 02 '22 at 16:05
  • @ubfan1 That's .bashrc being sourced by bash, not ls. – chepner Jun 02 '22 at 16:06
  • Then something is very wrong with your configuration. ls does not look for or execute .bashrc. – chepner Jun 02 '22 at 16:12
  • @Terrance I had somehow overlooked that bit of text. Thank you. – MDeBusk Jun 02 '22 at 22:48
  • @chepner I have nothing so interesting in there right now. – MDeBusk Jun 02 '22 at 22:50
  • .bashrc is also only used by bash, but all login shells (ash, csh, ksh, fish) use .profile. – OrangeDog Jun 03 '22 at 10:30
  • Neither csh nor fish use .profile, as they use entirely different syntax. – chepner Jun 03 '22 at 12:15
16

~/.profile is only called when you first log into your account. Any changes you make after that it would be wise to log out and back in so settings take effect. ~/.bashrc is called every time you launch a terminal window. There is another profile file and it is in the /etc/ directory. The main difference between the two is that the /etc/profile is called when anyone logs into the system, and the ~/.profile is called when only the user logs in.

If your export lines are only used in a terminal session then I would add them to the ~/.bashrc file as they are only valid during the terminal (bash) session. But, if you want them to be there with or without a terminal open, add them to the ~/.profile file, but like @chepner has stated do not put bash specific commands in the ~/.profile file.

If you screw up these files, there are default files stored in the /etc/skel/ directory that you can copy back over to your home directory. Also, the /etc/skel/ files are also used when you boot to a LiveUSB/CD/DVD, so if you modify those on your live media and then when it completes the boot you can have your own variables set.

Terrance
  • 41,612
  • 7
  • 124
  • 183
  • this doesn't seem to the question about setting exported variables in .profile vs. in .bashrc? – ilkkachu Jun 02 '22 at 12:00
  • @ilkkachu I don't understand what you are asking here. Please elaborate. And also, this really is a question that is based more on opinion since it is a preference thing. – Terrance Jun 02 '22 at 12:43
  • The question seemed to ask about moving exported variables from .bashrc to .profile, and about the differences between them, or any breakage the move might cause. And I didn't see that addressed in the answer at all. It's true that ~/.profile is read only for login shells, but what does it mean for the environment variables? – ilkkachu Jun 02 '22 at 13:15
  • @ilkkachu I still have no clue what you are talking about. ~/.profile is not read only as I can modify it at anytime while I am logged into a system. As far as exporting lines go, you need to look at what they are used for. ~/.bashrc is used during a terminal session only that is running bash. ~/.profile is used even by the GUI and terminal sessions and is only called one time at log in. It really depends on what their export lines are. – Terrance Jun 02 '22 at 13:23
  • 1
    Assuming that your terminal sessions are all started from your login shell (or a descendent thereof), they will inherit any environment variables defined in .profile. The key is to understand that it's not terminal windows, per se, that use .bashrc, but the interactive non-login shells that they typically run by default. – chepner Jun 02 '22 at 13:32
  • On macOS, for example, terminal sessions tend to start login shells, because the terminal emulator itself is not being started from a login shell. – chepner Jun 02 '22 at 13:33
  • @chepner I can, and in fact, keep sourcing my ~/.bashrc file over and over again in a terminal (bash) session and it will duplicate settings over and over again and really mess things up. But, if I close the session and re-open it, those variables are cleaned up and reset. Honestly, if you really want, feel free to write your own answer here. – Terrance Jun 02 '22 at 13:35
  • "~/.profile is only called when you first log into your account." Only if the only login shell you start is the initial shell when you log in. – chepner Jun 02 '22 at 13:37
  • .bashrc isn't really intended to be repeatedly sourced. It's for configuring an interactive shell on start up. – chepner Jun 02 '22 at 13:38
  • 2
    Original answer was actually better, being less technical and answering the question, i.e., what to put in .profile and what in .bashrc, more directly. – vanadium Jun 02 '22 at 15:52
  • @vanadium Do you think I should just go back to the original answer with a few changes added? – Terrance Jun 02 '22 at 15:54
  • Actually, yes, although in the mean time, chepner filled the gap very nicely. – vanadium Jun 02 '22 at 15:56
  • @vanadium Thank you! :) And, done! – Terrance Jun 02 '22 at 16:02
  • 1
    Upvoted. It is now to the OP to choose ;) – vanadium Jun 02 '22 at 18:12
  • @vanadium It's a bit of a shame that "there can be only one!" – MDeBusk Jun 02 '22 at 22:58
  • @MDeBusk I am OK with anyway that you choose. I am here to help. :-) – Terrance Jun 02 '22 at 23:17
12

chepner's answer covers the overall principles. There are a few other subtleties that you want to take note of.

First, login shells do not source .bashrc even if they are interactive. Thus, you probably want a .bash_profile that contains code like the following:

interactive() { [[ $- = *i* ]]; }
interactive && . $HOME/.bashrc || echo 1>&2 "ERROR: .bashrc failed with retval $?"

This won't make much difference on a console login into a graphical environment, but will make quite a difference on a text console or ssh login.

You don't necessarily need to do the interactive test; I actually source .bashrc even for non-interactive shells (though this is arguably not a good idea in some circumstances).

Note that if you use non-Bash shells as well, you'll also need a .profile with whatever environment variables etc. you want for those. Bash will ignore your .profile when you have a .bash_profile, but of course the latter can source the former.


Not every fresh login (i.e., a shell created not by your user id but by a root process that has authenticated you) is considered a "login shell." In particular, giving a command to an ssh invocation (ssh somehost.example.com 'echo $-') will not be a "login shell" and .bash_profile/.bash_login/.profile will not be sourced. Here's a table showing what's considered a "login" or interactive shell and what's executed for various invocations

State Invocation
Login (L) argv[0][0] == '-', --login
Interactive (I) -i, stdin is tty
Remote (R) stdin is "network connection" (see below)
  • '•' = set; blank = not set; '-' = not checked or don't care.
  • inh: Inherits environment from a parent (other than init); i.e., it's not a "fresh login" and the parent may expect that the environment be kept intact.
  • p: sources /etc/profile then first of ~/.bash_profile, ~/.bash_login, ~/.profile
  • rc: sources /etc/bash.bashrc then ~/.bashrc
L I R inh p rc Example
- ssh host.com (sshd sets argv[0]="-bash")
- ssh host.com </dev/null (sshd sets argv[0]="-bash")
bash -i, bash on tty
bash hello.sh, bash -c echo foo,
ssh host.com 'echo $-' (ssh runs bash -c 'echo $-)

The table comes from this document; there's some further information there that might also be useful. (Anybody who feels something there should be in this answer should feel free to edit this and copy it in.)

cjs
  • 324