I have a bash script a.sh that looks like this:
#!/bin/bash
echo $#
echo $1
and a script b.sh that looks like this:
#!/bin/bash
source ./a.sh
If I call ./a.sh I'm correctly getting 0 and an empty line as output. When calling ./a.sh blabla I'm getting 1 and blabla as output.
However when I call ./b.sh blabla I'm also getting 1 and blabla as output, even though no argument was passed to a.sh from within b.sh.
This seems to be related to the use of source (which I have to use since in my real use case, a.sh exports some variables). How can I avoid arguments from b.sh being propagated to a.sh? I thought about using eval $(a.sh) but this makes my echo statements in a.sh fail. I thought of using shift to consume the arguments from b.sh before calling a.sh but I don't necessarily know how many arguments there are.
The root of the problem is an anomaly in how the source command works. From the bash man page, in the "Shell Builtin Commands" section:
. filename [arguments]
source filename [arguments]
[...] If any arguments are supplied, they become the positional parameters when filename is executed. Otherwise the positional parameters are unchanged.
...which means you can override the main script's arguments by supplying different arguments to the sourced script, but you can't just not pass arguments to it.
Fortunately, there's a workaround; just source the script in a context where there are no arguments:
#!/bin/bash
wrapperfunction() {
source ./a.sh
}
wrapperfunction
Since no arguments are supplied to wrapperfunction, inside it the arg list is empty. Since a.sh's commands are run in that context, the arg list is empty there as well. And variables assigned inside a.sh are available outside the function (unless they're declared as local or something similar).
(Note: I tested this in bash, zsh, dash, and ksh93, and it works in all of them -- well, except that dash doesn't have the source command, so you have to use . instead.)
Update: I realized you can write a generic wrapper function that allows you to specify a filename as an argument:
sourceWithoutArgs() {
local fileToSource="$1"
shift
source "$fileToSource"
}
sourceWithoutArgs ./a.sh
The shift command removes the filename from the function's arg list, so it's empty when the file actually gets sourced. Well, unless you passed additional arguments to the function, in which case those will be in the arg list and will get passed on... so you can actually use this function to replace both the without-args and the with-args usage of source.
(This works in bash and zsh. If you want to use it in ksh, you have to remove local; and to use it in dash, replace source with .)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With