Why is /etc/profile not invoked for non-login shells?

Login and non-login shell defined as:

su - $USER # will give you a login shell
bash # will give you a non-login shell

/etc/profile is not invoked for non-login shells, such as when you start konsole (kde). /etc/profile is only invoked for login shells.

Why is that? Please explain, because I like to understand the rationale of this.


Solution 1:

/etc/profile is invoked only for login shells because that is its specific purpose.

If you want a command to run for interactive shells that are not login shells, and you're using bash, put it in ~/.bashrc or /etc/bash.bashrc.

The purpose of the "profile" files is to contain commands that ought to be run for login shells only. These files are:

  • /etc/profile, run by all Bourne-compatible shells (including bash and dash) when started as a login shell.

  • Scripts in /etc/profile.d.

    This is for Bourne-style shells, but it's not coded into the shell executable itself. Rather, commands in /etc/profile calls them. For example, on my Ubuntu 12.04 system, /etc/profile includes these lines:

    if [ -d /etc/profile.d ]; then
      for i in /etc/profile.d/*.sh; do
        if [ -r $i ]; then
          . $i
        fi
      done
      unset i
    fi
    
  • .profile in the user's home directory, run by Bourne-compatible shells when started as a login shell (unless overridden, see below).

  • .bash_profile or .bash_login in the user's home directory. These are ignored by shells other than bash. But if .bash_profile exists, bash runs it instead of .profile. If .bash_profile doesn't exist but .bash_login exists, that is run instead of .profile.

    (But it is common for .bash_profile or .bash_login, when it exists, to be written so as to *explicitly call .profile.)

    The benefit of shell-specific profile files is that they can contain commands or syntax that are only valid for that shell. For example, I can use the [[ evaluation operator in .bash_profile/.bash_login but if I use it in .profile and then log in with dash as my shell, it will fail.

What Should Go In "profile" Files

"profile" files should contain commands that ought only run once, at the beginning of login. (This includes graphical logins, as they start with a login shell, too.) If a shell is interactive, the user running it is probably logged on, and so it probably has an ancestor (that started it, or started what started it, or started that, etc.) that was a login shell.

You might want to run a command only once because:

  1. there is no reason to run it more than one time per login, it would be inefficient, or
  2. it would produce an undesired result, to run it more than once per login.

As an example of the second situation, where an undesirable result would occur, consider these lines, which appear by default in every user's ~/.profile:

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

Suppose you SSH'd in, ran another shell (say, zsh), at some point found you wanted to temporarily go back to bash but keep your environment (so ran bash again while in zsh), and then ran a program like mc that runs a shell as part of its interface. If bin exists in your home folder and your username is james, your PATH in the innermost shell is something like:

/home/james/bin:/home/james/bin:/home/james/bin:/home/james/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

That's inefficient and (much more importantly) makes it hard to understand the contents of PATH.

This is by no means a disaster, though. As far as I can tell, if every interactive shell sourced "profile" files, nothing terrible would happen, in the default configuration. However, since the purpose of "profile" files is to contain commands to just run once per login, a user or administrator may add commands to a profile that must only run when starting a login shell.

Where to Put Commands for Every Interactive Shell to Run

If you are using bash, there are files for commands that are to be run in every interactive shell:

  • /etc/bash.bashrc
  • .bashrc in the user's home directory.

This is most commonly used for commands that

  1. affect only the environment of the shell in which they run--not even child shells, or
  2. ought to run even when this is not the login shell.

For example, command-line tab-completion should generally be enabled whether or not bash was the login shell. So this appears in ~/.bashrc:

if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
    . /etc/bash_completion
fi

There, 1 and 2 both apply: this does not carry over to other shells run inside this one, and tab-completion should work in bash even if I logged in with a different shell.

Where to Put Commands for Login Shells and Interactive non-Login Shells

If you're using bash and want a command to run in login shells and interactive shells and that are not login shells, it is generally sufficient to put it in /etc/bash.bashrc or ~/.bashrc. This is because, by default, /etc/profile and ~/.profile run them explicitly. For example, ~/.profile has:

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi
fi

(Similarly, /etc/profile sources /etc/bash.bashrc for bash.)

Thus both "profile" and "rc" files run when you start an interactive bash shell (whether or not it's a login shell).

Where to Put Commands to Run in non-Interactive Shells

You probably do not want to specify any commands for all non-interactive shells to run; they would run every time a script is run (provided the script is run by the shell you configure to run them).

This can cause substantial breakage. If you're going to do this, and there is not an administrator account on the system besides the one you're using, you might want to create one; that can make it easier to fix mistakes.

In bash, the "rc" files are actually run whether the shell is interactive or not. However, at the top they say:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

So, if you need commands to run automatically even in non-interactive shells like the ones that run to execute scripts, you can add your commands before those lines.

Starting a Login Shell

Logging in starts a login shell. If you want a shell started after that to behave as a login shell, start it with the -l flag (stands for login). For example:

  • sh -l
  • bash -l
  • pdksh -l

That's the best way to start a login shell (without logging in) unless you want to start one as another user. Then, use:

  • sudo -i for root (use sudo -s for a non-login, interactive root shell)
  • sudo -u username -i for any user
  • su - username for non-root users (use su username for a non-login, interactive root shell)

What's an initial login shell?

An initial login shell is the same as a login shell. Everywhere this answer says "login shell" it could say "inital login shell" (except in this section, which would already have stoped making sense).

One reason for the term inital login shell is that login shell is also used in a different sense--to identify which program is used as the shell that is executed by logging on. This is the sense of login shell used to say:

  • "OpenBSD's default login shell is ksh; in Ubuntu, it's bash."
  • "You can change your login shell with chsh."

Further Reading

  • "Configuration files for shells" in "Unix shell" (Wikipedia)
  • bash manpage
  • Full bash manual (GNU)