Under normal circumstances, Bash only sources your
.bashrc
when it's run as an interactive non-login shell; for example, this is what the Bash manual says about startup files . Well, it is most of what the manual says, because there is an important exception, which the Bash manual describes as '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, [...]
(You can tell how old this paragraph of the manual is because of how much prominence it gives to rshd . Also, note that this specific phrasing about standard input presages my discovery of when bash doesn't do this .)
As the result of recent events , I became interested in discovering exactly how Bash decides that it's being run in the form of '
ssh host command
' and sources your
.bashrc
. There turn out to be two parts to this answer, but the summary is that if this is enabled at all, Bash will always source your
.bashrc
for non-interactive commands if you've logged in to a machine via SSH .
First, this feature may not even be enabled in your version of Bash, because it's a non-default configuration setting (and has been since Bash 2.05a, which is pretty old ). Debian and thus Ubuntu turn this feature on, as does Fedora, but the FreeBSD machine I have access to doesn't in the version of Bash that's in its ports. Unsurprisingly, OmniOS doesn't seem to either. If you compile Bash yourself without manually changing the relevant bit of config-top.h , you'll get a version without this.
(Based on some digging, I think that Arch Linux also builds Bash without enabling this, since they don't seem to patch config-top.h. I will leave it to energetic people to check other Linuxes and other *BSDs.)
Second, how it works is actually very simple. In practice, a non-interactive Bash decides that it is being invoked by SSHD if either
$SSH_CLIENT
or
$SSH2_CLIENT
are defined in the environment. In a robotic sense this is perfectly correct, since OpenSSH's
sshd
puts
$SSH_CLIENT
in the environment when you do '
ssh host command
'. In practice it is wrong, because OpenSSH sets
$SSH_CLIENT
all the time, including for logins . So if you use SSH to log in somewhere,
$SSH_CLIENT
will be set in your shell environment, and then any non-interactive Bash will decide that it should source
~/.bashrc
. This includes, for example , the Bash that is run (as '
bash -c ...
') to execute commands when you have a Makefile that has explicitly set '
SHELL=/bin/bash
', as Makefiles that are created by the GNU autoconfigure system tend to do.
As a result, if you have ancient historical things in a
.bashrc
, for example clearing the screen on exit , then surprise, those things will happen for every command that
make
runs. This may not make you happy. For situations like Makefiles that explicitly set '
SHELL=/bin/bash
', this can happen even if you don't use Bash as your login shell and haven't had anything to do with it for years.
(Of course it also happens if you have perfectly modern things there and expect that they won't get invoked for non-interactive shells, and you do use Bash as your login shell. But if you use Bash as your login shell, you're more likely to notice this issue, because routine ordinary activities like '
ssh host command
' or '
rsync
host:/something .
' are more likely to fail, or at least do additional odd things.)
PS: This October 2001 comment in
variables.c
sort of suggests why support for this feature is now an opt-in thing.
PPS: If you want to see if your version of Bash has this enabled, the simple way to tell is to run
strings
on the binary and see if the embedded strings include '
SSH_CLIENT
'. Eg:
; /etc/fedora-release Fedora release 29 (Twenty Nine) ; strings -a /usr/bin/bash | fgrep SSH_CLIENT SSH_CLIENT
So the Fedora 29 version does have this somewhat dubious feature enabled. Perhaps Debian and Fedora feel stuck with it due to very long-going backwards compatibility, where people would be upset if Bash stopped doing this in some new Debian or Fedora release.
Sidebar: The actual code involved
The code for this can currently be found in
run_startup_files
in
shell.c
:
/* get the rshd/sshd case out of the way first. */
if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 &&
act_like_sh == 0 && command_execution_string)
{
#ifdef SSH_SOURCE_BASHRC
run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) ||
(find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0);
#else
run_by_ssh = 0;
#endif
[...]
Here we can see that the current Bash source code is entirely aware that no one uses rshd any more, among other things.