如果我有这个命令:
expr 2 * 4
它不起作用,因为*
通配符将被评估,所以我必须转义它:
expr 2 \* 4
但如果我有以下两种情况:
let var1=2*4
var2=$((2*4))
那么*
通配符将不会被评估,因此不需要转义它。
为什么*
在上述两种情况下通配符没有得到评估?我的猜测是,在使用andbash
时根本不会评估它。let
$(())
如果是这种情况,是否还有其他情况下通配符不会被求值?
答案1
在 中var2=2*4
,由于空格(元字符),并且2
pwd 中不存在以 a 、一些字符和 4 开头的文件。
该行使用空格作为分隔符分割为标记。
这没有空格(在它之后=
被认为是引用的):
$ var1=2*4
但这确实:
$ echo 2 * 4
并且*
成为一个标记并被扩展(作为文件名扩展)。
为了避免使用引用的特殊效果*
:
$ expr 2 \* 4
8
$ expr 2 "*" 4
8
$ expr 2 '*' 4
8
然而,这确实扩展了:
$ touch 2good4
$ echo 2*4
2good4
在 中echo $((2*4))
,构造内部的内容$((…))
被解释为算术展开式。任何一个都会做同样的事情(即使有空格):
$ echo $((2*4))
8
$ echo $(( 2 * 4 ))
8
$ echo $(( 2 * 4 ))
8
答案2
您看到的行为已得到解释在这里进行通配符和这里是为了扩展并且man let
bash 的参数规范页面知道给“let”
当 bash 看到*
它自己时,它会执行路径名扩展除了
其中扩展返回 null 结果,在这种情况下它返回文字字符串。
echo 2 * 4 #echo 2, the contents of the pwd and then 4
如果*
前面有一个标记,告诉它使用差异形式或扩展(数学/命令/参数或根本不扩展),则它使用相关方法。所以
let var1=2*4 #pass the **arithmetic** argument var1=2*4 to the let command because bash knows let takes special characters (% * etc) as arguments
var2=$((2*4)) #bash parses the string as far as $ and sees an instruction to expand
#bash then sees (( the which specifies arithmetic expansion specifies
正如你所说
echo 2 \* 4 #perform no expansion
答案3
如果你想知道原因,你应该知道 bash 可以做什么以及 bash 如何解析命令:
首先,* 在 bash 中可以做什么:
作为文件名 glob 中的通配符
乘
正则表达式中出现 0 次或多次
...
以及 bash 如何解析命令(大大简化,实际上非常复杂):
参数替换
命令替换
通配
...
假设你有a.txt b.txt在当前目录下,在expr 2 * 4
参数替换->命令替换->通配后,就会变成
expr 2 a.txt b.txt 4
哪个原因不会返回正确的答案。
然而在 中expr 2 \* 4
,* 是一个字符!参数替换、命令替换、通配符都不会影响它。所以 expr 得到 3 个参数 2 * 4,它会返回 8。
在 中let var1=2*4
,bash 在通配之前进行算术替换,通配后的命令是let var1=8
。如果当前目录中有 2a4 2b4 并且不使用 let,只需
var1=2*4
echo $var1
它将返回 2a4 2b4,而不是 8。
在 中var2=$((2*4))
,bash 也进行算术替换,因为 (()) 是算术运算符。
附:如果您希望 * 成为算术运算符,请将其保留在 (()) $[] 中或使用 let,例如:
((var1 = 2 * 4)) or var1=$((2 * 4))
var1=$[2 * 4]
允许使用空格!