为什么 shell 脚本赋值语句等号两边不能有空格?

这篇博客来自于我的知乎回答,我觉得这个问题很有意思,就在这里重新发一遍。

每个初学 shell 的人应该都会有这个疑问。这个规定是为了防止歧义,因为 bash 和其他语言的不同点在于,bash 可以在脚本中直接运行命令,而且其中空格最重要的作用就是将参数列表中的参数区分开。

例如,依次输入下面这些命令:

$ echo fuck > =
$ echo shit > 1
$ cat = 1

最后一条命令 cat = 1 运行的结果是什么?

$ cat = 1
fuck
shit

我们刚刚做了什么?前两条命令把字符串 fuck 和字符串 shit 分别写到了本目录下的文件名为 =1 的文件中,然后 cat = 1 表示运行 cat 程序,并把 =1作为参数。

因此,既然允许 = 这样的文件名存在,既然允许 = 本身可以作为某些命令的参数,就不应该允许赋值语句的等号两边都有空格。(PS:其实可以规定 = 作为参数的时候必须带双引号,通过本行第二个 token 来判断是否为赋值语句,但应该是因为历史遗留原因不能改吧)

那只有一边有空格可以么?我知道 cat =1 也是一样的问题,但如果只把左边的空格去掉,像 cat= 1 这样子不行么?

答案依然是不行。我们换一个相对直观的例子,比如我们想把 ls 字符串赋给变量 CC,按照这样就可以写 CC= ls,这样结果是什么呢?

答案是:这个的运行结果与 ls 命令的运行结果一模一样,其实 CC= ls 代表在环境变量 CC="" 的情况下运行 ls 命令,只要允许这样写来指定命令运行时的环境变量,只要允许变量可以在赋值中省略 value 作为“空字符串”,只省略左空格的写法也是不行的。

当然,可能也有人会说如果我有个程序就叫 a=1 呢?也可以做个小实验,例如把 ls 命令复制一份名字改成 a=1sudo cp /usr/bin/ls /usr/bin/a=1,然后再执行 a=1,发现啥也没发生,再检查 $a 的值时发现为 1,说明变量赋值优先级是高于命令的;但如果真的想执行这个程序的话,只需要在两边加双引号,例如 "a=1" ,在一定程度上规避了这个问题。

PS:这个规定并不是对于所有 shell 的,而仅仅对于 Bourne family 的 shell,例如 ksh, bash, ash/dash, zshyash,对于其他的 shell 并不成立,比如 rc 就是采用了我上面说的 = 作为参数时用引号引起来,其他时候都作为赋值语句。

新增评论