Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash inserting extra, incorrect single quotes when quoting argument list

Tags:

bash

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?

like image 542
Kat Avatar asked Feb 13 '23 12:02

Kat


1 Answers

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, and world" 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.

like image 128
Charles Duffy Avatar answered Apr 28 '23 01:04

Charles Duffy