我有下面的例子。
#!/bin/bash
ARGUMENTS="-executors 1 -description \"The Host\" "
# call1
# error: parameter Host" is not allowed
java -jar swarm-client.jar $ARGUMENTS
# call2
# works fine with eval
eval java -jar swarm-client.jar $ARGUMENTS
在 中$ARGUMENTS
,我引用了一个论点。我不明白为什么通过转义引号对参数进行分组在call1
.我不明白为什么有eval
必要解决引用问题。
我想我不明白shell中命令评估的过程和顺序。你能给我解释一下吗?
答案1
你不传递带引号的参数对于命令,您可以传递参数。
当您输入:
cmd arg1 arg2
shell 以自己的语法解析该行,其中空格是单词分隔符,并使用cmd1
,cmd
和arg1
作为arg2
参数进行调用。
笔记:cmd
在其参数中不接收任何空格字符,空格只是 shell 语言语法中的运算符。
就像在 C 中一样,您func("foo", "bar")
在运行时编写 ,func
接收两个指针参数,它看不到任何(
or,
或"
或 空格字符。
shell 语法的一部分也是引用。"
用于能够包含包含 shell 语法的其他部分的字符的单词。
当你这样做时:
cmd "arg 1" arg2
cmd
接收cmd
,arg 1
和arg2
作为参数。它看不到任何"
字符。它们"
用于防止空格在 shell 语法中被视为单词分隔符。
现在,当你这样做时:
cmd $VAR
这与执行以下操作不同:
cmd the content of the variable
如果是这样,你就会遇到麻烦:
VAR='foo; reboot'
echo $VAR
例如。
在类似 Bourne 的 shell 中, 的内容$VAR
不会作为单个参数逐字传递给cmd
任何一个(不幸的是;它已在其他一些 shell 中得到修复,如、rc
和在较小程度上)。相反,它会受到拆分和通配符 ( ) 的影响,并将生成的单词传递给.es
fish
zsh
split+glob
cmd
拆分是根据特殊$IFS
变量中的字符进行的,默认为空格、制表符和换行符。
对于$ARGUMENTS
包含 的内容,-executors 1 -description "The Host"
它分为-executors
、1
、-description
和"The
。Host"
由于这些单词都不包含通配符,因此 glob 部分不适用,因此这些单词将被传递到cmd
.
在这里,您可以使用split+glob
运算符,并使用这些单词中未出现的字符作为分割部分的分隔符:
ARGUMENTS='-executors|1|-description|The Host'
IFS='|'
cmd $ARGUMENTS
或者更好的是,对于支持它们的 shell(例如bash
),使用数组,您可以在其中包含一个包含所有这些参数的变量。
eval
是评估shell代码。因此,另一个选择是包含ARGUMENTS
shell 代码(shell 语法中的文本,而不是参数列表),并将其传递给eval
解释。但请记住引用变量以避免 split+glob 运算符:
eval "cmd $ARGUMENTS"
答案2
当引号位于另一个字符串内时,bash 将其视为另一个字符。使用数组代替:
args=(-executors 1 -description "The Host")
java -jar swarm-client.jar "${args[@]}"
有关此问题的详细讨论,请参阅“我试图将命令放入变量中,但复杂的情况总是失败!”。
另外,为变量使用小写或混合大小写的名称。系统使用所有大写字母作为变量名称,您不想意外覆盖其中之一。
怎么运行的
定义数组后args
,单独的带引号的字符串将成为数组中单独的项。我们可以通过使用declare -p arrayname
检查数组中的内容来看到这一点:
$ args=(-executors 1 -description "The Host")
$ declare -p args
declare -a args=([0]="-executors" [1]="1" [2]="-description" [3]="The Host")
正如我们所看到的,该字符串The Host
是数组中的元素 3。
当bash扩展特殊形式时"${args[@]}"
,数组的每个元素都成为一个单独的元素。单词。这样,字符串The Host
就保持为一单词即使它包含空格。