23

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
3
  • Check the INVOCATION section of the bash manual, I think that when bash is a login shell 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,
    – HBruijn
    Commented Oct 23, 2018 at 10:42
  • Truly so. Read man ssh: "If a command is specified, it is executed on the remote host instead of a login shell." Commented Feb 25, 2020 at 11:08

2 Answers 2

27

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.

6
  • 2
    This should be the accepted answer! Following this answer, I put the commands I wanted to be run over SSH at the top of ~/.bashrc (before case $- in etc) and then everything worked over SSH!!
    – Jake Levi
    Commented Feb 16, 2022 at 18:18
  • Very insightful, resolved my issue. Thank you!
    – jayant
    Commented Jul 20, 2022 at 18:51
  • Commenting these lines didn't resolve my issue; I'll try moving this block to the bottom of .bashrc ..
    – icedwater
    Commented Apr 12, 2023 at 2:29
  • 1
    It's important when doing scp and ssh that the .bashrc file when source does not produce any output. If it sends any output it screws up the scp and ssh commands and they will not work.
    – PatS
    Commented Apr 28, 2023 at 1:14
  • This can be shortened to a one-liner: [[ $- != *i* ]] && return
    – darkfeline
    Commented May 25, 2023 at 1:03
8

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.

You must log in to answer this question.

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