When SSHing into a machine using bash as the shell, launching asynchronous sub-shells in that shell causes the sub-shell to execute /etc/bash.bashrc and .bashrc. Reproducer: 1. Put the line "echo Hello from .bashrc" at the very beginning of the .bashrc on <somehost> 2. ssh into <somehost> 3. Execute the following command: > bash -c 'echo SHLVL=$SHLVL ; echo \$-=$-' & wait [1] 27048 Hello from .bashrc SHLVL=1 $-=hBc [1]+ Done bash -c 'echo SHLVL=$SHLVL ; echo \$-=$-' Note that there is output from .bashrc, despite $- reporting that the shell is not interactive. Also note that SHLVL stays at one, this seems to be the cause for this behaviour. Executing the same command without '&' works as expected: > bash -c 'echo SHLVL=$SHLVL ; echo \$-=$-' SHLVL=2 $-=hBc Here SHLVL is correctly increased to 2, and no output from .bashrc is seen. Unsetting the environment variable SSH_CLIENT causes yet another variation in behaviour: > bash -c 'echo SHLVL=$SHLVL ; echo \$-=$-' & wait [1] 29257 SHLVL=1 $-=hBc [1]+ Done bash -c 'echo SHLVL=$SHLVL ; echo \$-=$-' Here SHLVL incorrectly stays at 1, but no output from .bashrc is seen. The problem is most likely related to the following change (see file CHANGES) between version bash-4.4-release and bash-5.0-alpha. [...] f. Fixed a bug that caused SHLVL to be incremented one too many times when creating subshells. [...] In shell.c, function run_startup_files() is the code responsible for executing .bashrc in certain circumstances. If the shell finds the environment variable SSH_CLIENT to be defined and SHLVL is less than 2, .bashrc is executed. My guess is that the fix mentioned above has the (probably unintended) consequence that SHLVL is no longer increased for asynchronous sub-shells and therefore the code executing .bashrc is triggered incorrectly.
This same problem happens for SUBSHELL_PAREN, SUBSHELL_COMSUB,
SUBSHELL_PIPE and SUBSHELL_COPROC as well as SUBSHELL_ASYNC
Test case (This must be sourced to see SHLVL == 1 which causes
/etc/bash.bashrc to be sourced in a shell started by ssh)
$ cat test.sh
test1="bash -c 'echo \$SHLVL'"
test2="{ bash -c 'echo \$SHLVL' ; }"
do_test()
{
echo "****** Testing $1 $2"
eval $2
wait
echo "***** DONE *****"
echo
}
do_coproctest()
{
echo "****** Testing COPROC $1"
eval coproc $1
IFS= read -ru ${COPROC[0]} x; printf '%s\n' "$x"
wait
echo "***** DONE *****"
echo
}
do_test BASIC "$test1"
do_test BASIC "$test2"
#ASYNC
do_test ASYNC "$test1 &"
do_test ASYNC "$test2 &"
#PAREN
do_test PAREN "( $test1 )"
do_test PAREN "( $test2 )"
#COMSUB
do_test COMSUB "echo \$( $test1 )"
do_test COMSUB "echo \$( $test2 )"
#PIPE
do_test PIPE "$test1 | cat"
do_test PIPE "$test2 | cat"
#PROCSUB
do_test PROCSUB "cat <( $test1 )"
do_test PROCSUB "cat <( $test2 )"
#COPROC
do_coproctest "$test1"
do_coproctest "$test2"
##############
Proposed patch:
diff -urN bash-5.1.orig/execute_cmd.c bash-5.1/execute_cmd.c
--- bash-5.1.orig/execute_cmd.c 2020-10-12 14:16:13.000000000 +0000
+++ bash-5.1/execute_cmd.c 2020-10-12 14:16:13.000000000 +0000
@@ -5487,7 +5487,12 @@
#if 0 /* TAG: bash-5.2 psmith 10/11/2020 */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
(subshell_environment & SUBSHELL_PIPE) == 0)
#else
- if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
+ if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
+ (subshell_environment & SUBSHELL_ASYNC) == 0 &&
+ (subshell_environment & SUBSHELL_PAREN) == 0 &&
+ (subshell_environment & SUBSHELL_COMSUB) == 0 &&
+ (subshell_environment & SUBSHELL_PIPE) == 0 &&
+ (subshell_environment & SUBSHELL_COPROC) == 0)
#endif
adjust_shell_level (-1);
#############
It seems absurd that:
bash -c 'echo $SHLVL'
{ bash -c 'echo $SHLVL' ; }
should both have SHLVL == 2 but if they are executed async then the
first one runs with SHLVL == 1 but not the second.
The proposed patch causes them all to run at SHLVL == 2 which then
avoids the issue of SHLVL == 1 causing /etc/bash.bashrc to be sourced
when running under ssh.
It also fixes issues with make running in interactive shells
unexpectedly.
Without the fix:
$ cat Makefile.test
all:
echo $$SHLVL
$ make -f Makefile.test
echo $SHLVL
1
$ make -f Makefile.test | cat
echo $SHLVL
0
$
This does not change the behaviour reported in #702559
#################
N.B. The sourcing of /etc/bash.bashrc is most easily seen by:
$ ( bash -uc : )
/etc/bash.bashrc: line 8: PS1: unbound variable
$
This issue of unbound variables in bash.bashrc was reported in #941248
###################
Results of running test.sh in bullseye:
(If it is sourced the results are, as expected, one lower)
# ./test.sh
****** Testing BASIC bash -c 'echo $SHLVL'
3
***** DONE *****
****** Testing BASIC { bash -c 'echo $SHLVL' ; }
3
***** DONE *****
****** Testing ASYNC bash -c 'echo $SHLVL' &
2
***** DONE *****
****** Testing ASYNC { bash -c 'echo $SHLVL' ; } &
3
***** DONE *****
****** Testing PAREN ( bash -c 'echo $SHLVL' )
2
***** DONE *****
****** Testing PAREN ( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing COMSUB echo $( bash -c 'echo $SHLVL' )
2
***** DONE *****
****** Testing COMSUB echo $( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing PIPE bash -c 'echo $SHLVL' | cat
2
***** DONE *****
****** Testing PIPE { bash -c 'echo $SHLVL' ; } | cat
3
***** DONE *****
****** Testing PROCSUB cat <( bash -c 'echo $SHLVL' )
3
***** DONE *****
****** Testing PROCSUB cat <( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing COPROC bash -c 'echo $SHLVL'
2
***** DONE *****
****** Testing COPROC { bash -c 'echo $SHLVL' ; }
3
***** DONE *****
##################
with the proposed patch applied:
****** Testing BASIC bash -c 'echo $SHLVL'
3
***** DONE *****
****** Testing BASIC { bash -c 'echo $SHLVL' ; }
3
***** DONE *****
****** Testing ASYNC bash -c 'echo $SHLVL' &
3
***** DONE *****
****** Testing ASYNC { bash -c 'echo $SHLVL' ; } &
3
***** DONE *****
****** Testing PAREN ( bash -c 'echo $SHLVL' )
3
***** DONE *****
****** Testing PAREN ( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing COMSUB echo $( bash -c 'echo $SHLVL' )
3
***** DONE *****
****** Testing COMSUB echo $( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing PIPE bash -c 'echo $SHLVL' | cat
3
***** DONE *****
****** Testing PIPE { bash -c 'echo $SHLVL' ; } | cat
3
***** DONE *****
****** Testing PROCSUB cat <( bash -c 'echo $SHLVL' )
3
***** DONE *****
****** Testing PROCSUB cat <( { bash -c 'echo $SHLVL' ; } )
3
***** DONE *****
****** Testing COPROC bash -c 'echo $SHLVL'
3
***** DONE *****
****** Testing COPROC { bash -c 'echo $SHLVL' ; }
3
***** DONE *****
This bug is still present in bookworm. Attached updated patch.