The problem because source execute file in current environment. And in bash, if no positional parameters is provided, they are unchanged. From bash Bourne Shell Builtins man page:
. (a period)
. filename [arguments]
Read and execute commands from
the filename argument in the current shell context. If filename does
not contain a slash, the PATH variable is used to find filename. When
Bash is not in POSIX mode, the current directory is searched if
filename is not found in $PATH. If any arguments are supplied, they
become the positional parameters when filename is executed. Otherwise
the positional parameters are unchanged. The return status is the exit
status of the last command executed, or zero if no commands are
executed. If filename is not found, or cannot be read, the return
status is non-zero. This builtin is equivalent to source.
POSIX defines dot (source is synonym to dot in bash):
The shell shall execute commands from the file in the current
environment.
And it also explained the KornShell version of dot can take optional arguments, that are set to the positional parameters:
The KornShell version of dot takes optional arguments that are set to
the positional parameters. This is a valid extension that allows a dot
script to behave identically to a function.
So when you call source bar inside _import function, you don't provide any positional parameters, so they are unchanged. They are identical with _import function scope, $@ contains bar and $# is 1 (Because you run _import bar).
When you call source bar outside _import function scope, it's identical to global scope (or foo script). In this case, because you run ./foo, you run foo without any arguments, $@ is null and $# is zero.
_import barprints_import bar::1:bar:, but does not explain why_import bar fooprints_import bar foo::1:foo:, instead of_import bar foo::2:bar foo:– Rafael Eyng Jan 03 '20 at 02:59_import bar fooabove,source "$@"expands tosource "bar" "foo", so the positional parameters are changed: instead ofsourceingbarwith no parameters (the 'unchanged' scenario),baris nowsourceed with 1 parameterfoo(the 'changed' scenario); it just so happens that because it goes from zero to one parameter, the output is confusing. This confusion stems from_import bar, wheresourcereveals that it is aware of what is passed to the calling_importfunction; for further clarity/confusion, change$@to$1;P – Darren Bishop Jun 21 '20 at 10:42