31

Bash Manual says:

Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If Bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable.

This Bash sources ~/.bashrc:

ssh user@host :

But this Bash sources ~/.bash_profile:

ssh user@host

I don't see a difference in these two commands according to the spec. Isn't stdin connected to a network connection in both cases?

  • 2
    While it's not what you're asking about, I'd like to note that it's considered good practice to source .bashrc from .bash_profile. That way, the settings from .bashrc will be applied regardless of whether bash is started as a login shell or a non-login shell. – Ilmari Karonen Dec 25 '16 at 3:00
53

A login shell first reads /etc/profile and then ~/.bash_profile.

A non-login shell reads from /etc/bash.bashrc and then ~/.bashrc.

Why is that important?

Because of this line in man ssh:

If command is specified, it is executed on the remote host instead of a login shell.

In other words, if the ssh command only has options (not a command), like:

ssh user@host

It will start a login shell, a login shell reads ~/.bash_profile.

An ssh command which does have a command, like:

ssh user@host :

Where the command is : (or do nothing).
It will not start a login shell, therefore ~/.bashrc is what will be read.


Remote stdin

The supplied tty connection for /dev/stdin in the remote computer may be an actual tty or something else.

For:

$ ssh sorontar@localhost
/etc/profile sourced

$ ls -la /dev/stdin
lrwxrwxrwx 1 root root 15 Dec 24 03:35 /dev/stdin -> /proc/self/fd/0

$ ls -la /proc/self/fd/0
lrwx------ 1 sorontar sorontar 64 Dec 24 19:34 /proc/self/fd/0 -> /dev/pts/3

$ ls -la /dev/pts/3
crw--w---- 1 sorontar tty 136, 3 Dec 24 19:35 /dev/pts/3

Which ends in a TTY (not a network connection) as the started bash sees it.

For a ssh connection with a command:

$ ssh sorontar@localhost 'ls -la /dev/stdin'
sorontar@localhost's password: 
lrwxrwxrwx 1 root root 15 Dec 24 03:35 /dev/stdin -> /proc/self/fd/0

The list of TTY's start the same, but note that /etc/profile was not sourced.

$ ssh sorontar@localhost 'ls -la /proc/self/fd/0'
sorontar@localhost's password:
lr-x------ 1 sorontar sorontar 64 Dec 24 19:39 /proc/self/fd/0 -> pipe:[6579259]

Which tells the shell that the connection is a pipe (not a network connection).

So, in both the test cases, the shell is unable to know that the connection is from a network and therefore does not read ~/.bashrc (if we only talk about the connection to a network). It does read ~/.bashrc, but for a different reason.

| improve this answer | |
  • Wouldn't the no-arg case also qualify being run with its standard input connected to a network connection and thus have ~/.bashrc read? – Cyker Dec 24 '16 at 11:29
  • @Cyker That is assuming that the shell will have the stdin connected to a network. Why do you assume that ? (Answer edited, please read). – Isaac Dec 24 '16 at 23:44
  • The edited part is Interesting. Looks like ssh doesn't bother with a pty when simply executing a command. – Cyker Dec 25 '16 at 12:31
  • Tip: In CentOS a SSH root login reads the ~/.bash_profile but not ~/.bashrc. – joseluisq Mar 4 at 14:03
13

You ask about the "why" not the "how", so I'll try to answer from that perspective. The following will be a good deal of rationale of why things happened in the past to result in how they happen today.


The reason for having two different startup files ("profile" and "rc") is that in the past the common way to work on a machine was:

  1. Login from some kind of real terminal or other workstation and get a login shell. This shell will invoke /etc/profile and ~/.profile and setup the environment for the user.

  2. Invoke the environment the user wants to enter. This environment could be Xorg, but in most cases it was a multiplexer such as GNU screen.

  3. The environment (e.g. GNU screen) would then invoke extra (non-login) shells which inherit the environment from the parent login shell.

That was the common way of logging in to a UNIX machine during the time when csh and bash were being developed. Therefore it was deemed wasteful to read ~/.profile again in the shells that were inheriting the environment anyway.

bash then added ~/.bashrc for extra configuration for these non-login shells. csh (and tcsh) never added any kind of "rc" file for non-login shells. Note that csh/tcsh are not shells compatible with the bourne shell (which is part of POSIX) whilst bash is. Another bourne compatible shell, ksh, added an environment variable (called ENV), which, if was defined would be used as a run commands ("rc") file for non-login ksh.

So yeah, newer versions of bourne shells added the extra configuration file as a convenience for aliases and other quick options that would be present inside the shells muxed by GNU screen (or similar) but not present in the shell you get when you first enter the machine.

With the raise of graphical display managers (GDMs) the differentiation between the "profile" files and "rc" files became meaningless because the GDM would have its own initialization files (e.g. ~/.xinit and ~/.xsession). Then, shells stated from inside the GDM could be login or non-login shells depending of a user's whims, and the case in which a non-login shell would always have a parent that is a login shell is not true anymore.

Extra

One of my favourite tables about shell startup file comparison shows how bourne shell compatible shells use the profile files whilst other shells doesn't. This is because in the past the initial shell (the one that started the muxer) needed to be a bourne compatible shell.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.