Difference between `su` and `su -` in Bash on Linux, or Login Shell and Interactive Shell
Although this topic has been discussed from B.C., I want to re-summarize the difference between su
and su -
while explaining the meaning of login shell and interactive shell.
What is Login Shell and Interactive Shell?
A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option.
An interactive shell is one started without non-option arguments, unless -s is specified, without specifying the -c option, and whose input and output are both connected to terminals (as determined by isatty(3)), or one started with the -i option.1
When we have normally logged in to a Linux system, the shell where we are in is called login (and also, interactive) shell. We can confirm this by issuing the below command:
$ echo $0 -bash # <- the first character of the output is '-'
We can go into non-login, interactive shell by simply type bash
after logging in as above.
$ echo $0 -bash # <- we are in login shell $ printenv SHLVL 1 $ $ bash $ # <- it looks nothing has happened... $ $ echo $0 bash # <- but we are now in non-login, interactive shell $ printenv SHLVL 2
OK. Then, what is the difference between the two? We can interact with them apparently without any difference at all.
The difference becomes clear when you consider the initializing process of bash. Haven't you been in trouble determining where to put a line of export PATH="/path/to/your/bin:$PATH"
? Some document says ~/.bash_profile
and the other says ~/.profile
, ~/.bashrc
, or something like them. In fact, it depends on your situation and the criterion is below:2
- When launched, login shell firstly searches for
/etc/profile
and loads it if it exists. After that, the shell searches for the below files in that order. When it runs into one, it loads that one and stops searching. At most only one of the below is loaded.-
~/.bash_profile
-
~/.bash_login
-
~/.profile
-
- When launched, interactive shell searches for
~/.bashrc
. If it exists, the shell loads it.
So, it is better to put environment variable declarations on ~/.bash_profile
or one of its siblings. ~/.bashrc
is a good place to put shell options, functions, or aliases. Since ~/.bashrc
is NOT automatically loaded by login shell, many Linux distribution has default settings that load ~/.bashrc
inside /etc/profile
, ~/.bash_profile
, or so. As an example, Ubuntu's ~/.profile
is below:
# ~/.profile: executed by the command interpreter for login shells. # This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login # exists. # see /usr/share/doc/bash/examples/startup-files for examples. # the files are located in the bash-doc package. # the default umask is set in /etc/profile; for setting the umask # for ssh logins, install and configure the libpam-umask package. #umask 022 # if running bash if [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi # set PATH so it includes user's private bin if it exists if [ -d "$HOME/bin" ] ; then PATH="$HOME/bin:$PATH" fi # set PATH so it includes user's private bin if it exists if [ -d "$HOME/.local/bin" ] ; then PATH="$HOME/.local/bin:$PATH" fi
In the above script loads $HOME/.bashrc
if it exists.
What is the Difference between su
and su -
?
Now that we understand login shell and interactive shell, we can grasp the meaning of su
and su -
.
su
means substitute user and we can switch to another user after we have logged in. Assume we are user alice
and we want to switch to user bob
, we can do that as follows:
alice@host:~$ su bob Password: *** # <- input Bob's password bob@host:/home/alice$ # <- we have switched to Bob, but we are still in Alice's home directory
The problem here, however, is that the user is still in Alice's home directory and doesn't have the environment set so that the shell looks as if Bob directly logged in to the system.
In a practical world, Bob would have his specific environment variables. For example, assume Bob has installed tj/n to manage multiple versions of Node.js and has an environment variable $N_PREFIX
set to /home/bob/n
and has $N_PREFIX/bin
at the beginning of $PATH
in ~/.profile
like below.
# /home/bob/.profile # ...some other settings... # Initialize tj/n export N_PREFIX="$HOME/n" export PATH="$N_PREFIX/bin:$PATH"
When Alice switches to Bob like the previous example, she would expect to have node
installed with tj/n in $PATH
. In this case, however, she doesn't have it and cannot smoothly use node
like Bob would do.
alice@host:~$ printenv N_PREFIX # <- $N_PREFIX not set alice@host:~$ which n >/dev/null 2>&1 && which node >/dev/null 2>&1 && node -v || echo "node unavailable" node unavailable alice@host:~$ su bob Password: *** bob@host:/home/alice$ printenv N_PREFIX # <- $N_PREFIX still not set bob@host:/home/alice$ which n >/dev/null 2>&1 && which node >/dev/null 2>&1 && node -v || echo "node unavailable" node unavailable
In order to solve this problem, we can use su
's -
option. With this option, we can spawn a brand new bash as a login shell when we switch to another user as if we logged in to the system as the target user.
alice@host:~$ printenv N_PREFIX # <- $N_PREFIX not set alice@host:~$ which n >/dev/null 2>&1 && which node >/dev/null 2>&1 && node -v || echo "node unavailable" node unavailable alice@host:~$ su - bob Password: *** bob@host:~$ # <- unlike the above example, we are immediately in Bob's home directory bob@host:~$ printenv N_PREFIX /home/bob/n # <- $N_PREFIX is set bob@host:~$ which n >/dev/null 2>&1 && which node >/dev/null 2>&1 && node -v || echo "node unavailable" v14.15.5 # <- now, node is available
For this reason, using -
option is safer and more natural. It is generally recommended to use -
option with su
.
When we issue su
or su -
without target username, we can switch to the root user.
$ su - Password: *** # <- input root user's password # # <- switched to root user
In the above example, we type in root user's password. In some Linux distributions including Ubuntu, however, root user doesn't have password set by default. Still, in that case, we can switch to the root user if our user is a sudoer, who can invoke sudo
.
$ sudo su - Password: *** # <- input your password (depending on setting, you may not have to input at all) # # <- switched to root user
In conclusion, we can issue sudo su -
to switch to the root user without having to set root user's password while spawning root user's environment naturally.