为什么在这两种情况下不计算星号 (*) 通配符?

为什么在这两种情况下不计算星号 (*) 通配符?

如果我有这个命令:

expr 2 * 4

它不起作用,因为*通配符将被评估,所以我必须转义它:

expr 2 \* 4

但如果我有以下两种情况:

let var1=2*4

var2=$((2*4))

那么*通配符将不会被评估,因此不需要转义它。

为什么*在上述两种情况下通配符没有得到评估?我的猜测是,在使用andbash时根本不会评估它。let$(())

如果是这种情况,是否还有其他情况下通配符不会被求值?

答案1

在 中var2=2*4,由于空格(元字符),并且2pwd 中不存在以 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 letbash 的参数规范页面知道给“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 中可以做什么:

  1. 作为文件名 glob 中的通配符

  2. 正则表达式中出现 0 次或多次

    ...

以及 bash 如何解析命令(大大简化,实际上非常复杂):

  1. 参数替换

  2. 命令替换

  3. 通配

    ...

假设你有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]

允许使用空格!

相关内容