Why It Happens
When "$@" is expanded as part of a string, the preceding and following parts of that string are prepended to the first item in the "$@" list, and appended to the last item in that list.
So, when you run:
bash -c "source environment && foo $@"
...with "$@" containing the list launch --name this is a test, what you get is:
bash -c "source environment && foo launch" --name 'this is a test'
Only the launch -- the first array element -- becomes part of the bash -c argument that's parsed as source code; other members of the array then fill out $0, $1 and so forth in the copy of bash that gets invoked. And because the code source environment && foo launch doesn't look at $0, $1 or so forth, those arguments are subsequently ignored.
How To Fix It
Use single-quotes around the string you want executed as literal code. Arguments should be kept out-of-band from that code. Thus, with newlines added for (a perhaps excessive amount of) visual clarity around where each argument begins and ends:
foo() {
set -x
bash \
-xc \
'source environment && foo "$@"' \
"$0" \
"$@"
}
Let's break down exactly what it's passing as its argument vector, when you call foo "first argument" "second argument" in zsh with the above function definition (assuming that this is being called from a script named yourscriptname, and filling out $0 appropriately):
bash -xc 'source environment && foo "$@"' yourscriptname "first argument" "second argument"
...which, if we look at it as JSON-ish pseudocode, is an argument vector with the following contents:
[
"bash", # shell to run
"-xc", # -x: enable tracing; -c: next argument is code
"source environment && foo \"$@\"", # actual code to run
"yourscriptname", # new $0 value when that code is running
"first argument", # $1 value when that code is running
"second argument" # $2 value when that code is running
]
That's exactly what we want. bash is running code that uses "$@" to expand to the argument list it was passed itself, and that argument list is kept out-of-band from the code that's being run so you don't have injection vulnerabilities.
bash -c 'source environment && sq launch-ec2' --name 'this is a test'is putting--nameinto$0, andthis is a testinto$1... but the codesource environment && sq launch-ec2doesn't look at$0or$1at all, so of course they both get ignored!environmentshould be aded to the environment, consider using the-aflag to make all subsequently-assigned variables automatically exported before sourcing it.set -awill turn that on, but you can just add it to the bash command line's flags:bash -xac ...orbash -ac ...if you don't want-xonce you're done testing.source environment && exec foo "$@"-- theexecmeans the copy of bash doesn't stay in memory afterfoois started; and the copy offooinherits the PID, so signals that would otherwise go to your shell instead go directly tofoo.set -x) at the end which made the question unclear. I've fixed it now - it should befoo launchrather thansq launch-ec2.