Issue
I'm trying to make a program that will run Maven with certain arguments.
It's basically doing this:
ARGUMENTS=\""${@:2}"\"
mvn exec:java -Dexec.mainClass=$1 -Dexec.args=$ARGUMENTS
So running ./myScript.sh a b c
should result in running:
mvn exec:java -Dexec.mainClass=a -Dexec.args="b c"
But Maven is throwing errors about an unknown life cycle. Throwing set -x
in there tells me that the command is actually turning into:
mvn exec:java -Dexec.mainClass=a '-Dexec.args="b' 'c"'
Echoing $ARGUMENTS
gives the expected "b c"
. What's causing these extra quotes to be added and how can I fix this to get my intended results?
Solution
arguments="${*:2}"
mvn exec:java -Dexec.mainClass="$1" -Dexec.args="$arguments"
See BashFAQ #50 for a full explanation. That said:
"$@"
maintains the split between argv array elements -- which is to say that --foo="$@"
, when set -- hello world
, resolves to "--foo=hello" "world"
.
"$*"
combines argv array elements with the first character of IFS
between them, which is by default a space.
$*
, without the double quotes, does the same but fails to prevent string-splitting or glob expansion, thus combining all arguments, but then allowing the shell to split them apart again (and expand globs they contain) -- a behavior which is very rarely desirable.
arguments="\"${*:2}\""
creates a string -- "hello world"
. When you run -Dexec.args=$arguments
, this goes through several phases of processing (excluding ones irrelevant to your current dataset, such as glob expansion):
- Syntactic parsing occurs. It is at this stage, and only at this stage when the shell determines which characters are quoted and how. Because no literal quotes exist in
-Dexec.args=$arguments
, the only expansion is recorded as unquoted. - Expansion occurs. At this point, the expanded string look like
-Dexec.args="hello world"
- String-splitting occurs. Because the parsing stage already processed, recorded and removed syntactic quotes, any quotes remaining are data, and are processed into individual words. Thus,
-Dexec.args="hello
is a word, andworld"
is a word.
Compare this to the correct usage, -Dexec.args="$arguments"
(or its effective equivalent, "-Dexec.args=$arguments"
). At this point:
- Syntactic parsing occurs. This removes the double quotes around
$arguments
, and flags the expansion within as double-quoted. - Expansion occurs. If you still have literal quotes inside the
arguments
array, they're substituted in as data, making-Dexec.args="hello world"
the literal data passed to Maven, including the quote characters themselves. - String splitting occurs on expanded strings. In this case, because syntactic parsing flagged the expansion as double-quoted, it has no effect.
If you don't want literal quote characters passed to Maven (and you shouldn't!), simply omit them, and use only syntactic quotes, as done in the example at the top of this answer.
Answered By - Charles Duffy Answer Checked By - Robin (WPSolving Admin)