1

Whenever one reloads the shell without opening a new terminal, with exec $SHELL, bashrc files get executed again.

The question is:

Should commands intended to run at shell startup explicitly support this operation?

Of course, shell functions don't survive shell process replacement, but envvars do. As such, a usual side effect is stuff getting added into PATH multiple times.

To stay objective, I'm primarily interested whether there are any official guidelines regarding this.

  • 2
    I don't understand. The entire point of .bashrc is that it is read each time you start a new interactive shell. exec bash starts such a shell (although just bash is usually what you want). How else would it work? – terdon Sep 15 '20 at 12:52
  • @terdon What I mean is whether commands run from bashrc should detect if you are doing a shell reload and avoid doing things twice. – ivan_pozdeev Sep 15 '20 at 12:54
  • 5
    What is the difference? I mean, by definition, this file is executed whenever you start a new shell. You are starting a new shell, so why would they not be executed? Perhaps you have put things in ~/.bashrc that belong in ~/.profile instead? See Is there a ".bashrc" equivalent file read by all shells? and the links therein. – terdon Sep 15 '20 at 12:56
  • The difference is things can break if they don't detect this. E.g. in the case of PATH manipulation, entries may now be in a wrong order. – ivan_pozdeev Sep 15 '20 at 18:31
  • 2
    Yes, which is one of the reasons why PATH has no business being set in .bashrc. Read the answer I linked to above, PATH settings belong in .profile. .bashrc should only have things you want to run on each new shell you open. – terdon Sep 16 '20 at 08:01

2 Answers2

4

The .bashrc file is designed to hold things you want executed each time a new interactive shell is started. This isn't the right place to set your PATH variable, for example, you should only have things that you need to run on each shell initialization.

This is why we have two "families" of configuration files for bash:

  • /etc/bashrc, ~/.bashrc are read each time a new interactive shell that is not a login shell is started.

  • /etc/profile, ~/.bash_profile, ~/.bash_login, and ~/.profile (in that order) are sourced each time a new interactive login shell is started.

What this means in practice, for most systems, is that the ~/.profile group of files are read once, when you log in and then the ~/.bashrc files are read each time you open a new terminal or start a new interactive shell.

Therefore, if you see that things in your ~/.bashrc are being executed and causing you problems each time you start a new shell, that means your system has been misconfigured and you're using ~/.bashrc when you should be using ~/.bash_profile or ~/.profile. In other words, yes, .bashrc is absolutely supposed to be executed every time you start a new interactive non-login shell.

terdon
  • 242,166
  • "that means you have misconfigured your system" -- not me but Ubuntu maintainers then. They run shells in Gnome-terminal as non-login shells by default. – ivan_pozdeev Sep 16 '20 at 16:18
  • @ivan_pozdeev yes, that's normal. But the default Ubuntu .bashrc does not include any PATH commands. Precisely because "normal" shells (what you get when you open a new terminal in a GUI environment) are non-login shells. Debian (and by extension Ubuntu) have one odd thing in that the default .profile sources .bashrc, but not the other way around. So if your PATH is getting screwed up, it's because you or some other user of your machine added PATH settings to .bashrc where they don't belong. – terdon Sep 16 '20 at 16:28
  • But non-login shells don't run ~/.bash_profile, and the X session "shell" doesn't run it at its startup, either. So in this mode, I never have a chance to run the code in ~/.bash_profile -- which breaks your suggested way of action! – ivan_pozdeev Sep 16 '20 at 17:32
  • 1
    @ivan_pozdeev no, non login shells don't run it. Which is why you put environment variable definitions there: you run a login shell when you login and all your other shells inherit exported variables from it. Most GUI login managers also run ~/.profile. bash_profile is specific to bash though and is only needed if you want bash to be different from other login shells – terdon Sep 16 '20 at 18:54
1

You can use this function, found in /etc/profile

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            [ ! -d "$1" ] && return
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}

That detects if a directory is already in the PATH before adding it.

Use it in your ~/.profile or ~/.bash_profile:

path_munge /directory/to/add before
# or
path_munge /directory/to/add/also after

to prepend or append the wanted dir to PATH.

Note that /etc/profile unsets the function, so copy it to your own profile.

glenn jackman
  • 85,964