On my Arch install, /etc/bash.bashrc
and /etc/skel/.bashrc
contain these lines:
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
On Debian, /etc/bash.bashrc
has:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
And /etc/skel/.bashrc
:
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
According to man bash
, however, non-interactive shells don't even read these files:
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 com‐
mand were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file‐
name.
If I understand correctly, the *.bashrc
files will only be read if BASH_ENV
is set to point to them. This is something that can't happen by chance and will only occur if someone has explicitly set the variable accordingly.
That seems to break the possibility of having scripts source a user's .bashrc
automatically by setting BASH_ENV
, something that could come in handy. Given that bash will never read these files when run non-interactively unless explicitly told to do so, why do the default *bashrc
files disallow it?
Best Answer
This is a question that I was going to post here a few weeks ago. Like terdon, I understood that a
.bashrc
is only sourced for interactive Bash shells so there should be no need for.bashrc
to check if it is running in an interactive shell. Confusingly, all the distributions I use (Ubuntu, RHEL and Cygwin) had some type of check (testing$-
or$PS1
) to ensure the current shell is interactive. I don’t like cargo cult programming so I set about understanding the purpose of this code in my.bashrc
.Bash has a special case for remote shells
After researching the issue, I discovered that remote shells are treated differently. While non-interactive Bash shells don’t normally run
~/.bashrc
commands at start-up, a special case is made when the shell is Invoked by remote shell daemon:Example
Insert the following at the start of a remote
.bashrc
. (If.bashrc
is sourced by.profile
or.bash_profile
, temporarily disable this while testing):Run the following commands locally:
i
in$-
indicates that the shell is non-interactive.-
in$0
indicates that the shell is not a login shell.Shell functions defined in the remote
.bashrc
can also be run:I noticed that the
~/.bashrc
is only sourced when a command is specified as the argument forssh
. This makes sense: whenssh
is used to start a regular login shell,.profile
or.bash_profile
are run (and.bashrc
is only sourced if explicitly done so by one of these files).The main benefit I can see to having
.bashrc
sourced when running a (non-interactive) remote command is that shell functions can be run. However, most of the commands in a typical.bashrc
are only relevant in an interactive shell, e.g., aliases aren’t expanded unless the shell is interactive.Remote file transfers can fail
This isn’t usually a problem when
rsh
orssh
are used to start an interactive login shell or when non-interactive shells are used to run commands. However, it can be a problem for programs such asrcp
,scp
andsftp
that use remote shells for transferring data.It turns out that the remote user’s default shell (like Bash) is implicitly started when using the
scp
command. There’s no mention of this in the man page – only a mention thatscp
usesssh
for its data transfer. This has the consequence that if the.bashrc
contains any commands that print to standard output, file transfers will fail, e.g, scp fails without error.See also this related Red Hat bug report from 15 years ago, scp breaks when there's an echo command in /etc/bashrc (which was eventually closed as
WONTFIX
).Why
scp
andsftp
failSCP (Secure copy) and SFTP (Secure File Transfer Protocol) have their own protocols for the local and remote ends to exchange information about the file(s) being transferred. Any unexpected text from the remote end is (wrongly) interpreted as part of the protocol and the transfer fails. According to a FAQ from the Snail Book
SCP protocol details
For those interested in the details of how SCP works, I found interesting information in How the SCP protocol works which includes details on Running scp with talkative shell profiles on the remote side?:
Conclusion / TLDR
Most of the statements in a typical
.bashrc
are only useful for an interactive shell – not when running remote commands withrsh
orssh
. In most such situations, setting shell variables, aliases and defining functions isn’t desired – and printing any text to standard out is actively harmful if transferring files using programs such asscp
orsftp
. Exiting after verifying that the current shell is non-interactive is the safest behaviour for.bashrc
.