Linux – `ssh ` is a login shell, but `ssh ` is not

.bash-profilebashbashrclinuxssh

I've noticed that when I run a command directly on an SSH host using the ssh <host> <command> syntax, I see the output of .bashrc but not the output of .bash_profile (or .profile).

For instance, if I place the following command at the top of both files,

echo ${BASH_SOURCE[0]}

and manually source .bash_profile (which sources .bashrc in turn), I'll see

$ . .bash_profile
.bash_profile
.bashrc

This is the same output I see if I log into this computer remotely via SSH, using the ssh <host> form of the command. (And if I stow .bash_profile somewhere else temporarily, neither of these lines gets echoed.)

However, if I execute a command directly on the remote machine with the ssh <host> <command> form of ssh, then the output looks like this:

$ ssh <host> echo foo
/home/rlue/.bashrc
foo

My understanding is that the difference between .bash_profile and .bashrc is that the former is for login shells while the latter is for interactive, non-login shells.

I've concluded the following:

  1. ssh <host> sources only .bash_profile, while
  2. ssh <host> <command> sources only .bashrc, which means
  3. the former is a login shell and the latter is not.

Are these conclusions correct? Why is ssh <host> <command> treated as an interactive, non-login shell? Isn't SSH still logging into the remote machine to execute the command?

Best Answer

OpenSSH (most likely what you're running) decides whether or not to create a login shell, and it only does so if you are not running a specific command. From man ssh:

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

So it's an implementation choice for the ssh server whether it wants to create a login shell or not, and if you give a command to run, it does not.

While ssh does perform a login, if you're having it execute a command and exit, it's really much more similar to creating a shell just to run that command than it is to getting a login environment. It seems, given that, that the people writing OpenSSH decided to treat it like that sort of task.

They create a non-interactive, non-login shell to execute the command, because that's the spirit of running a command in another context/shell. Normally, though, non-interactive shells would not automatically source ~/.bashrc which is clearly happening here. bash is actually trying to help us out here. From the docs

Invoked by remote shell daemon

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. It will not do this if invoked as sh. The --norc option may be used to inhibit this behavior, and the --rcfile option may be used to force another file to be read, but neither rshd nor sshd generally invoke the shell with those options or allow them to be specified.