Bash在引用参数列表时插入额外的、不正确的单引号

Bash在引用参数列表时插入额外的、不正确的单引号,bash,Bash,我正在尝试制作一个程序,该程序将使用某些参数运行Maven 它基本上是这样做的: ARGUMENTS=\”“${@:2}\” mvn exec:java-Dexec.mainClass=$1-Dexec.args=$ARGUMENTS 因此,运行/myScript.sh a b c应导致运行: mvn exec:java-Dexec.mainClass=a-Dexec.args=“bc” 但是Maven在一个未知的生命周期上犯了错误。在那里抛出set-x告诉我命令实际上变成了: mvn ex

我正在尝试制作一个程序,该程序将使用某些参数运行Maven

它基本上是这样做的:

ARGUMENTS=\”“${@:2}\”
mvn exec:java-Dexec.mainClass=$1-Dexec.args=$ARGUMENTS
因此,运行
/myScript.sh a b c
应导致运行:

mvn exec:java-Dexec.mainClass=a-Dexec.args=“bc”
但是Maven在一个未知的生命周期上犯了错误。在那里抛出
set-x
告诉我命令实际上变成了:

mvn exec:java-Dexec.mainClass=a'-Dexec.args=“b”c”

回显
$ARGUMENTS
将给出预期的
“bc”
。是什么原因导致这些额外的引号被添加,我如何解决这些问题以获得预期的结果?

这似乎是引用问题。使用BASH数组尝试以下操作:

arr=( "$@" )
mvn exec:java -Dexec.mainClass="$1" -Dexec.args="${arr[*]:1}"
请参阅以获取完整的解释。也就是说:

“$@”
维护argv数组元素之间的拆分——也就是说,
--foo=“$@”
设置--hello world时,解析为
“--foo=hello”“world”

“$*”
将argv数组元素与它们之间的
IFS
的第一个字符组合在一起,默认情况下是一个空格

$*
在没有双引号的情况下也会这样做,但无法阻止字符串拆分或全局扩展,从而合并了所有参数,但允许shell再次拆分它们(并扩展它们包含的全局)——这是一种非常不可取的行为

arguments=“\”${*:2}\”
创建一个字符串--
“hello world”
。当您运行
-Dexec.args=$arguments
时,这将经历几个处理阶段(不包括与当前数据集无关的阶段,例如glob扩展):

  • 进行语法分析。在这个阶段是,并且只有在这个阶段shell确定引用哪些字符以及如何引用。由于
    -Dexec.args=$arguments
    中不存在文字引号,因此唯一的扩展记录为unquote
  • 发生膨胀。此时,扩展的字符串看起来像
    -Dexec.args=“hello world”
  • 发生字符串拆分。因为解析阶段已经处理、记录和删除了语法引号,所以任何剩余的引号都是数据,并被处理成单个单词。因此,
    -Dexec.args=“hello
    是一个单词,
    world”
    是一个单词
将其与正确用法相比较,
-Dexec.args=“$arguments”
(或其等效有效用法,
“-Dexec.args=$arguments”
)。在这一点上:

  • 进行语法分析。这将删除
    $arguments
    周围的双引号,并将其中的扩展标记为双引号
  • 发生膨胀。如果
    参数
    数组中仍有文字引号,则它们将被替换为数据,使
    -Dexec.args=“hello world”
    成为传递给Maven的文字数据,包括引号字符本身
  • 字符串拆分发生在扩展的字符串上。在这种情况下,因为语法分析将扩展标记为双引号,所以它没有效果

如果您不希望将文字引号字符传递给Maven(您不应该这样做!),只需省略它们,并仅使用语法引号,如本答案顶部的示例所示。

Bash没有插入任何内容。有关行为的完整解释,请参阅。稍微简短一点的解释:在语法分析过程中使用引号,这发生在扩展之前,这发生在字符串拆分之前。因此,试图在扩展阶段将引号放在适当的位置,以期修改字符串拆分注定要失败。相反,您希望修复语法引号,而不是将引号作为数据插入……也就是说:
arguments=${*:2};java-Dexec.args=“$arguments”
。硬编码文字引号通常是错误的,除非您计划
eval
命令——这几乎总是错误的。顺便说一下,被接受的答案暗示:最好不要对纯shell变量使用所有大写变量名,以免与环境变量发生潜在冲突。需要在
$1
周围加引号。另外,如果所有参数都希望连接成一个字符串,那么相应的运算符是
*
,而不是
@
。是的,我没有注意到,现在添加了它。谢谢。@Mike,对于你想要的行为,它必须是
*
,而不是
@
,你需要引用。(也就是说,“你想要的行为”是非常不幸的,因为它意味着扔掉数据)。要验证是否需要在
@
上使用
arr=(foo-bar-baz);printf“@:%s\n”Dexec.args=“${arr[@]:1}”;printf“*:%s\n”Dexec.args=“${arr[*]:1}”
编辑后,这是完全正确的;我对是否保留自己的答案意见分歧。
arguments="${*:2}"
mvn exec:java -Dexec.mainClass="$1" -Dexec.args="$arguments"