565

I understand the basic difference between an interactive shell and a non-interactive shell. But what exactly differentiates a login shell from a non-login shell?

Can you give examples for uses of a non-login interactive shell?

3
  • 97
    I think the question is better phrased as "Why do/should we care to differentiate login and non-login shells?" Many places on the web already tell us what are the differences, in terms of what startup files each read; but none of them seems to answer the "why" in a satisfactory and convincing way. Example use cases where you definitely do not want one or the other behaviour would be great.
    – Kal
    Commented Apr 15, 2013 at 3:49
  • 17
    @Kal This would have to be a different question, since no answer here actually covers that. Edit : Actually, here it is : WHY a login shell over a non-login shell?. Commented Aug 14, 2018 at 13:15
  • 1
    What makes a program a shell? What makes it a login shell? Does the kernel care?
    – masterxilo
    Commented Jun 24, 2022 at 16:20

8 Answers 8

510

A login shell is the first process that executes under your user ID when you log in for an interactive session. The login process tells the shell to behave as a login shell with a convention: passing argument 0, which is normally the name of the shell executable, with a - character prepended (e.g. -bash whereas it would normally be bash). Login shells typically read a file that does things like setting environment variables: /etc/profile and ~/.profile for the traditional Bourne shell, ~/.bash_profile additionally for bash, /etc/zprofile and ~/.zprofile for zsh, /etc/csh.login and ~/.login for csh, etc.

When you log in on a text console, or through SSH, or with su -, you get an interactive login shell. When you log in in graphical mode (on an X display manager), you don't get a login shell, instead you get a session manager or a window manager.

It's rare to run a non-interactive login shell, but some X settings do that when you log in with a display manager, so as to arrange to read the profile files. Other settings (this depends on the distribution and on the display manager) read /etc/profile and ~/.profile explicitly, or don't read them. Another way to get a non-interactive login shell is to log in remotely with a command passed through standard input which is not a terminal, e.g. ssh example.com <my-script-which-is-stored-locally (as opposed to ssh example.com my-script-which-is-on-the-remote-machine, which runs a non-interactive, non-login shell).

When you start a shell in a terminal in an existing session (screen, X terminal, Emacs terminal buffer, a shell inside another, etc.), you get an interactive, non-login shell. That shell might read a shell configuration file (~/.bashrc for bash invoked as bash, /etc/zshrc and ~/.zshrc for zsh, /etc/csh.cshrc and ~/.cshrc for csh, the file indicated by the ENV variable for POSIX/XSI-compliant shells such as dash, ksh, and bash when invoked as sh, $ENV if set and ~/.mkshrc for mksh, etc.).

When a shell runs a script or a command passed on its command line, it's a non-interactive, non-login shell. Such shells run all the time: it's very common that when a program calls another program, it really runs a tiny script in a shell to invoke that other program. Some shells read a startup file in this case (bash runs the file indicated by the BASH_ENV variable, zsh runs /etc/zshenv and ~/.zshenv), but this is risky: the shell can be invoked in all sorts of contexts, and there's hardly anything you can do that might not break something.

I'm simplifying a little, see the manual for the gory details.

26
  • 5
    Could you give example how to run bash as a non-interactive login shell? Commented Jun 16, 2013 at 8:47
  • 17
    @PiotrDobrogost echo $- | bash -lx Commented Jun 16, 2013 at 12:11
  • 4
    I don't know if this is true in general, but I want to note that when I open a new terminal (on osx using default settings), I get a login shell even though I never type in my username or password. Commented Aug 28, 2015 at 22:55
  • 9
    @KevinWheeler On OSX, by default, the Terminal application runs a login shell. (As I explain, the program that starts the shell decides whether the shell acts as a login shell.) That's not the normal way to do things. Commented Aug 28, 2015 at 23:01
  • 4
    @IAmJulianAcosta If FOO is an environment variable (i.e. .profile contains export FOO=something) then it's available to all subprocesses, including foo.sh. If you change .profile to export FOO=something_else then ./foo.sh will still print something until the next time you log in. Commented Oct 6, 2016 at 15:13
93

To tell if you are in a login shell:

prompt> echo $0
-bash # "-" is the first character. Therefore, this is a login shell.

prompt> echo $0
bash # "-" is NOT the first character. This is NOT a login shell.

In Bash, you can also use shopt login_shell:

prompt> shopt login_shell
login_shell     off

(or on in a login shell).

Information can be found in man bash (search for Invocation). Here is an excerpt:

A login shell is one whose first character of argument zero is a -, or one started with the --login option.

You can test this yourself. Anytime you SSH, you are using a login shell. For Example:

prompt> ssh user@localhost
user@localhost's password:
prompt> echo $0
-bash

The importance of using a login shell is that any settings in /home/user/.bash_profile will get executed. Here is a little more information if you are interested (from man bash)

"When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior."

1
  • 14
    From bash manual, A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option.. According to this definition, echo $0 couldn't determine whether a shell is in login shell or not because when we start a shell with bash --login, $0 is set as bash(filename used to invoke Bash), not -bash.
    – rosshjb
    Commented Jul 8, 2020 at 19:29
31

I'll elaborate on the great answer by Gilles, combined with Timothy's method for checking login shell type.

If you like to see things for yourself, try the snippets and scenarios bellow.

Checking whether shell is (non-)interactive

if tty -s; then echo 'This is interactive shell.'; else echo 'This is non-interactive shell.'; fi

Checking whether shell is (non-)login

If output of echo $0 starts with -, it's login shell (echo $0 output example: -bash). Otherwise it's non-login shell (echo $0 output example: bash).

if echo $0 | grep -e ^\- 2>&1>/dev/null; then echo "This is login shell."; else echo "This is non-login shell."; fi;

Let's combine the two above together to get both pieces of information at once:

THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; 
THIS_SHELL_LOGIN_TYPE='non-login'; 
if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; 
if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi;
echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"

Scenarios:

Typical SSH session without special options

ssh [email protected]
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-1083-aws x86_64)

ubuntu@ip-172-31-0-70:~$ THIS_SHELL_INTERACTIVE_TYPE='non-interactive';
ubuntu@ip-172-31-0-70:~$ THIS_SHELL_LOGIN_TYPE='non-login';
ubuntu@ip-172-31-0-70:~$ if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi;
ubuntu@ip-172-31-0-70:~$ if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi;
ubuntu@ip-172-31-0-70:~$ echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"

interactive/login

Running script or executing explicitly via new shell

ubuntu@ip-172-31-0-70:~$  bash -c 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; 
echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

interactive/non-login

Running local script remotely

ssh [email protected] < checkmy.sh
Pseudo-terminal will not be allocated because stdin is not a terminal.
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-1083-aws x86_64)

non-interactive/login

Running a command over ssh remotely

ssh [email protected] 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

non-interactive/non-login

Running a command over ssh remotely with -t switch

You can explicitly request interactive shell when you want to run command remotely via ssh by using -t switch.

ssh [email protected] -t 'THIS_SHELL_INTERACTIVE_TYPE='non-interactive'; THIS_SHELL_LOGIN_TYPE='non-login'; if tty -s; then THIS_SHELL_INTERACTIVE_TYPE='interactive'; fi; if echo $0 | grep -e ^\- 2>&1>/dev/null; then THIS_SHELL_LOGIN_TYPE='login'; fi; echo "$THIS_SHELL_INTERACTIVE_TYPE/$THIS_SHELL_LOGIN_TYPE"'

interactive/non-login

Note: On topic why running command remotely is not login shell more info here.

1
  • This answer helped me to understand and see that when I SSH into a remote server with PuTTY it is in fact a login shell and an interactive shell (which is kind of confusing).
    – BadHorsie
    Commented Mar 3, 2023 at 20:54
27

In a login shell, argv[0][0] == '-'. This is how it knows it's a login shell.

And then in some situations it behaves differently depending on its "login shell" status. E.g. a shell, that is not a login shell, would not execute a "logout" command.

1
  • 9
    According to man bash, with emphasis added, "A login shell is one whose first character of argument zero is a -, or one started with the --login option."
    – Wildcard
    Commented Jan 23, 2017 at 11:49
22

A shell started in a new terminal in a GUI would be an interactive non-login shell. It would source your .bashrc, but not your .profile, for example.

6

Gilles' answer is great, but was tricky to follow for me, so here's the gist of what I personally needed to know in a simpler language.

  • A login shell is the shell given to the user upon their login. So only one of it will exist after the user has logged in.

  • A non-login shell is a shell invoked without the interference of the login process. Non-login shells do not need to be one per login, so you may get any number of them after you log in.

The distinction between the two lets the users bind the tasks that they need to be executed only once per their login (e.g. some heavy tasks) to the startup of their login shell, and avoid having them fired upon the creation of every new non-login shell (of which they might need numerous ones). Things will be much clearer (hopefully) through the two examples below.

Example 1:

Create a new user first (DON'T switch to it afterwards):

sudo adduser foo

then become root:

sudo -i

and add this line to /home/foo/.profile:

echo "Echoed on start of $USER's login shell"

Now switch the new user using su like this (Guess what will happen first):

su foo

You won't see anything echoed. You might have expected the echo you added to the file /home/foo/.profile to run, because this file is a login-triggered file, but the problem is not with this file, the problem is you never logged in to foo at all (yes, although it prompted you for foo's password). So although you can switch to other users using the command su <username>, it's not a "login" whatsoever. It's just that after switching, whenever you run commands, that user's ID and group ID will be used instead of yours'. I.e. according to the man su:

su allows to run commands with a substitute user and group ID.

So how can we login to a user (and get the login shell of that user as well) when switching to them? We read in the man su that we can use one of the options -, -l, --login !

-, -l, --login: Start the shell as a login shell...

So at this point if you exit, you will NOT be logged out of "foo", you will be logged out of your own user! So instead of exiting, let's switch back to our own user using su again:

su <your_own_user_name>

now run this command instead:

su --login foo

and give foo's password. This time, you will see this line echoed:

Echoed on start of foo's login shell

Now you can run exit too, to logout of foo and switch to your own login shell.

Example 2:

Add this line to your ~/.profile:

echo "start of $USER's login shell"

save, then open a new terminal, you will see it echoed. Now run:

echo $0

You must see (assuming you're on Bash):

-bash

As you see, it's the name of your shell program prepended with a dash, it means it is a login shell. Now run:

bash

You won't see anything echoed, but from this point on, you'll not be in the same Bash as before, you just received a new non-login bash shell. To confirm:

echo $0

Now it must not be prepended by a dash anymore, i.e.:

bash

Where you may want to use this? For instance, when you need to run some commands by passing them as an "argument" to bash. A famous example of this is using "sudo" behind a command that does redirection to a file requiring sudo privileges:

sudo echo 'example line' >> /path/to/file/requiring/sudo/privileges

Although sudo will run that command as root, still the redirection will be executed earlier (to open the file and prepare it for redirection), so a nice "Permission Error" will be printed and the whole command will fail. Instead, you can use a non-login shell as root and pass your whole command to it as a single argument. E.g.:

sudo bash -c "echo 'example line' >> /path/to/file/requiring/sudo/privileges"

Of course you don't want root's startup files to fire each and every time you execute such commands!

1
  • Something is wrong with example 2. One will only see the text echoed when they put it in ~/.bashrc and in both cases echo $0 will print bash and not -bash, as this is not a login shell. Commented Jun 18, 2022 at 0:03
6

This is old thread, but I have just found a concrete example of non-login interactive shell.

When I was using VSCode for remote developement on Linux VM, I realized that my environment variables in /etc/profile.d/env_file.sh were not picked up by VSCode integrated terminal, even after restarting VSCode and terminal itself. Output for $0 indicated that it is not a login shell.

It seems like, after connecting to remote Linux machine, VSCode was just starting one main login shell, but for every integrated terminal it launched just another /bin/bash process. You see the output of ps doesn't have a -, but that was inconclusive to decide whether the current shell is login or not.

$ echo $0
/bin/bash
$ ps  $$
  PID TTY      STAT   TIME COMMAND
 2274 pts/3    Ss     0:00 /bin/bash

When I login to server directly over SSH, I see -bash, that is a login shell.

~$ echo $0
-bash
~$ ps  $$
  PID TTY      STAT   TIME COMMAND
 2088 pts/2    Ss     0:00 -bash

Then I added shell argument (-l) option in VSCode. Now, output for echo $0 is same, but notice the $$ (Id of the current process, PID) /bin/bash is with -l ( --login ) option.

$ echo $0
/bin/bash
$ ps  $$
   PID TTY      STAT   TIME COMMAND
  2309 pts/3    Ss     0:00 /bin/bash -l

So, to check whether shell is login shell or not, you need to check both echo $0 and ps $$. Depending on the implementation, output should be either -bash or /bin/bash -l

1
  • I had the same problem with VSCode getting non-login shell and needed .profile to be sourced. So I followed your advice along with this thread and everything went well. Thanks.
    – José
    Commented Oct 4, 2023 at 7:54
1

Almost all the answers here are missing on how to check a login shell (only Timothy is close but still technically incorrect).

echo $0 is not a surefire way to check it. You can see this simply by calling bash --login or zsh --login. Even though it's a login shell echo $0 will not show the hyphen (-).

Hyphen indeed means a login shell, but no hyphen does not mean it's not a login shell.

A correct way to check

bash: shopt login_shell

zsh: setopt | grep login or set -o | grep login

These two are just examples. I'm sure other shells have their own commands to check shell options.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .