~/.bashrc is NOT sourced on ssh 'command'

At the end of .bashrc I added:

touch /tmp/bash_noninteractive_test

Run:

/usr/bin/ssh -v -C [email protected] 'ls'

On the host (logged in interactively before):

% ls -l /tmp/bash_noninteractive_test
ls: cannot access /tmp/bash_noninteractive_test: No such file or directory

I thought ~./bashrc is ALWAYS sourced in non-interactive shells, like over SSH? How do I fix that?

Systems affected:

% lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 14.04.5 LTS Release: 14.04 Codename: trusty

% lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 8.11 (jessie) Release: 8.11 Codename: jessie


Solution 1:

From the INVOCATION chapter of man bash:

When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist. This may be inhibited by using the --norc option. The --rcfile file option will force bash to read and execute commands from file instead of /etc/bash.bashrc and ~/.bashrc.

When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:

         if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

but the value of the PATH variable is not used to search for the filename.

So it appears that ~/.bashrc is never sourced on non-interactive shells, unless BASH_ENV points to it.

Depending on what you wish to actually achieve, you might consider enabling PermitUserRC and/or PermitUserEnvironment in /etc/ssh/sshd_config on your remote host, and then writing the desired environment variables in ~/.ssh/environment and/or the desired commands in ~/.ssh/rc.

NOTE: if you plan to use SSH X11 forwarding together with ~/.ssh/rc, you must include a snippet of code to explicitly feed the X11 session to the xauth -q - command; see the example in the SSHRC chapter of sshd(8) man page.

Solution 2:

Adding my bit to the old thread for information because the accepted answer is not the real reason for .bashrc not being read for an ssh command. I got the hint after reading this answer. The second last paragraph of the INVOCATION section in bash man page states that:

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 and ~/.bashrc, if these files exist and are 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.

Hence although the ssh command is run as a non-interactive user, .bashrc is still loaded.

But your OS may be adding checks at the top of .bashrc similar to the following (from Ubuntu 18.04) which exits .bashrc after the first few lines for a non-interactive invocation.

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

To prevent the checks, open .bashrc and comment those lines and your ssh commands will read the whole .bashrc which is the result, I suppose, you are expecting.