No need for export when running functions in subshell

I have a msource.sh script that will be sourced:

$ cat msource.sh 
#!/usr/bin/env sh
echo "($BASHPID) - sourced ${BASH_SOURCE[0]}" &>> "$logfile"
  # logfile is defined by the sourcing script
sourced_var="init sourced var with $BASHPID"

I have a script that will source msource.sh and call a function as is and in a subshell. It will then call another script mscript2.sh:

$ cat mscript.sh 
#!/usr/bin/env sh

logfile=mout.out
rm -f $logfile
source msource.sh

mfun() {
  echo "($BASHPID) in ${FUNCNAME}"  &>> "$logfile"
  echo "  avar: '$avar'" &>> "$logfile"
  echo "  sourced_var: '$sourced_var'" &>> "$logfile"
}

avar="$BASHPID - init"

echo "[mfun] basic call" &>> "$logfile"
mfun

echo -e "\n[mfun &] subshell call" &>> "$logfile"
mfun &
wait $!

## call mscript2.sh
echo -e "\n[mscript2] background call" &>> "$logfile"
bash mscript2.sh &
wait $!

# call mscript2.sh after exporting variables
echo -e "\n[mscript2 &] export and background call" &>> "$logfile"
export logfile
export avar
export sourced_var
bash mscript2.sh &
wait $!

I have another script, mscript2.sh that will be called by mscript.sh, as seen above:

$ cat mscript2.sh
#!/usr/bin/env sh

[ -z "${logfile:+x}" ] && logfile=mout2.out || true

echo "($BASHPID) - executing ${BASH_SOURCE[0]}" &>> "$logfile"
echo "  avar: '$avar'" &>> "$logfile"
echo "  sourced_var: '$sourced_var'" &>> "$logfile"

I run everything:

$ bash script.sh

I get the following outputs:

$ cat mout.out 
(13166) - sourced msource.sh
[mfun] basic call
(13166) in mfun
  avar: '13166 - init'
  sourced_var: 'init sourced var with 13166'

[mfun &] subshell call
(13174) in mfun
  avar: '13166 - init'
  sourced_var: 'init sourced var with 13166'

[mscript2 &] background call

[mscript2 &] export and background call
(13184) - executing mscript2.sh
  avar: '13166 - init'
  sourced_var: 'init sourced var with 13166'

and

$ cat mout2.out 
(13179) - executing mscript2.sh
  avar: ''
  sourced_var: ''

So if I call the function as is, the pid is the same and I don't need to source the msource.sh nor to export the variables. If I call the function in a subshell, sourcing msource.sh or exporting variables still is not required.

However, calling another script in asubshell loses all the variables and they need to be exported, even the logfile which will be redefined otherwise.

Can someone clarify what's going on? What's the difference between executing a function in a subshell and executing another script, that will also be launched in another subshell? Why don't a parent process' variables have to be exported to be passed to a subshelled function?


Solution 1:

This is by design. This answer on Unix & Linux SE explains the issue. The main point is:

A subshell is […] different from executing a script.