If we take a look at the following example:
# testing(){ echo hello;}
# testing
hello
# echo $(testing)
hello
# echo testing >script
# ./script
./script: line 1: testing: command not found
# source ./script
hello
# export -f testing
# ./script
helloIt turns out that a bash function needs to be exported only if you want to use it in a non-sourced script. I tried several levels of subshells, the behavior is the same. Can someone confirm this, because I find it contradictory with the claim that local variables do not exist in subshells.
2 Answers
source ./script does not create a subshell. The script is executed in the current shell. Nothing unexpected here.
However a command substitution like in echo $(testing) does create a subshell. If I get you right, you're surprised it works.
This is explained in Bash Reference Manual, Command Execution Environment section [emphasis mine]:
Command substitution, commands grouped with parentheses, and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment, except that traps caught by the shell are reset to the values that the shell inherited from its parent at invocation.
So it's a documented exception to the claim that local variables do not exist in subshells.
Functions become part of the process's environment by exporting them, just like variables. So to inherit them for subprocesses like a called script, they must be exported. Until it is not exported it is part of the shell's variables only.
Note: you can list the current environment with the env builtin command, and list the variables with set.
Note2: the source command does not create a subshell (subprocess), as others point it out also, so the source script works as you show it in the example. But the ./script command creates the subshell so you need to export the function.