BashTips:介绍“v=varecho$v”和“v=var;echo$v”的区别控制运算符。常用的控制运算符包括分号';'、管道运算符'|'、运算符'&&'或运算符'||'等。echo$v命令首先将v变量的值赋给var,然后使用echo命令打印v变量的值。但是今天查看安装wine命令的文章,提供了如下命令:WINEPREFIX=/home/.no1-winewine/home/.no1-wine/yyyy在这个命令中,赋值语句WINEPREFIX变量和后续的wine命令由空格而不是分号';'分隔。当然,这个命令本身是一个合法的命令,但是为什么用空格而不是分号分隔呢?这种写法和用分号分隔有什么区别?本着研究的精神,我通过查看GNUbash的在线帮助手册找到了这种写法的相关说明。详情如下。GNUbash在线帮助手册的链接是http://www.gnu.org/software/b…。后面贴出的英文说明均来自这个在线帮助链接。这是GNUbash的标准手册,权威可靠。在GNUbash在线帮助手册中,也使用了上述命令。“10.2CompilersandOptions”部分提供的编译bash命令如下:CC=c89CFLAGS=-O2LIBS=-lposix./configure可以看到,这个命令也是先提供变量赋值语句,再提供要执行的命令。以空格隔开。从源代码编译其他Linux软件时也有类似的写法。Bash的简单命令在GNUbash联机帮助手册的“3.2.1简单命令”一节介绍了简单命令的概念:简单命令是最常遇到的那种命令。它只是一系列由空格分隔的单词,由shell的一个控制运算符终止(请参阅定义)。第一个词通常指定要执行的命令,其余词是该命令的参数。这里提到一个简单的命令是一串由空白字符分隔的单词,由shell的控制运算符终止。一般来说,简单命令的第一个词是要执行的命令,后面的词是命令的参数。可以终止简单命令的控制运算符在“2定义”部分中进行了描述:控制运算符执行控制功能的令牌。它是换行符或以下之一:'||'、'&&'、'&'、';'、';;'、';&'、';;&'、'|'、'|&','(',或者')'。前面解释过,常用的控制操作符有分号';',管道操作符'|',还有操作符'&&',或者操作符'||'等等。结合这两条指令,一般来说,简单的命令以命令名开始,以控制操作符结束。不同的简单命令由控制运算符或换行符分隔。但是在上面提供的CC=c89CFLAGS=-O2LIBS=-lposix./configure命令中,赋值语句和要执行的命令并没有被控制操作符分开。这很奇怪。这也是本文要探讨的问题。Bash环境变量在GNUbash在线帮助手册的“3.7.4Environment”部分有介绍,介绍了先提供变量赋值语句,再提供执行命令的功能。具体说明如下:当一个程序被调用时,它被赋予一个字符串数组,称为环境。这是名称-值对的列表,格式为名称=值。如ShellParameters中所述,任何简单命令或函数的环境都可以通过为其添加参数分配前缀来临时增强。这些赋值语句只影响该命令看到的环境。可以看出,bash在执行命令时,会为执行命令的进程准备一些环境变量。环境变量由name=value形式的列表组成。在简单命令前提供变量赋值语句,以在执行命令时提供临时环境变量。给定的变量赋值语句只影响执行命令时的环境。这就是CC=c89CFLAGS=-O2LIBS=-lposix./configure所做的。该命令为CC、CFLAGS、LIBS这三个变量赋值,并在执行./configure命令时将这三个变量赋值语句添加到环境中。那么./configure命令就可以通过CC、CFLAGS、LIBS这三个变量名获取对应的值。这三个赋值语句只影响执行./configure命令时的环境,不影响当前shell的环境。也就是说,CC、CFLAGS、LIBS这三个变量在当前shell中并没有定义。如果写成CC=c89CFLAGS=-O2LIBS=-lposix的形式;./configure,使用分号';'将赋值语句和要执行的命令分开。如前所述,分号终止一个简单的命令。然后在赋值语句和执行的命令之间有两条简单的命令,每一条都有不同的进程环境。执行./configure命令时的环境变量不包括CC、CFLAGS、LIBS这三个变量。简单命令的展开顺序在GNUbash联机帮助手册的“3.7.1简单命令展开”一节中有描述:当执行一个简单命令时,shell从左到右执行如下展开、赋值和重定向.解析器标记为变量赋值(命令名称前面的那些)和重定向的词被保存以供以后处理。如果没有命令名称结果,则变量赋值会影响当前的shell环境。否则,变量将添加到执行命令的环境中,不会影响当前的shell环境。可以看出,当执行一个简单的命令时,命令名前面的变量赋值语句会被标记,留待后面处理。也就是说,在变量名前提供变量赋值语句确实是一种合法有效的方式。如果变量赋值语句后面没有跟任何命令名,那么赋值语句就会影响当前的shell环境。也就是说,分配的变量是在当前shell中定义的。该变量在当前shell中可见。如果变量赋值语句后跟命令名,该变量会在命令运行时加入环境变量,不会影响当前的shell环境。也就是说,正在分配的变量未在当前shell中定义。此变量在当前shell中不可见。子shell中环境变量的继承关系在GNUbash联机帮助手册的“3.7.3命令执行环境”一节中描述如下:当要执行除内建函数或shell函数之外的简单命令时,调用在由以下内容组成的单独执行环境中。除非另有说明,否则值都是从shell.shell标记为导出的变量和函数,以及为命令导出的变量,在环境中传递(参见环境)可以看出,bash在单独执行中执行简单的命令环境并从父shell继承一些值。其中,父shell中定义的变量默认不会被子shell继承。只有export命令导出的变量才会被子shell继承。验证“v=varecho$v”和“v=var;echo$v”命令之间的区别根据前面的描述,我们可以看出v=varecho$v和v=var;之间的区别。echo$vcommands是执行命令的环境变量不同。具体测试如下:$v=varecho$v$v=var;echo$vvar$v=var环境|grepvarv=var$v=var;环境|grepvar可以看到,v=varecho$vcommandprint的结果为空。该命令中定义了一个v变量,并将该变量添加到执行echo$v命令的环境变量中。那么echo命令可以通过变量名v获取到对应的值。但是echo命令本身的代码并没有获取到变量v的值,所以没有任何作用。这里的echo$v命令是获取当前shell中的v变量值,作为参数传递给echo命令。由于这种方式定义的v变量在当前shell中是不可见的,所以得到的值为空。最终打印结果为空。而v=var;echo$v命令打印v变量的值。这里有一个分号';'在v=var之后添加,使v=var成为一个简单的命令。根据前面的描述,v变量在当前shell中是可见的。之后echo$v命令就可以得到当前shell中的v变量值,作为参数传递给echo命令。echo命令接收传递的参数值并打印出“var”字符串。为了进一步验证,v=varenv|grepvar命令用env命令打印出运行时环境变量,过滤掉var关键字。可以看到,打印出来的环境变量中包含了赋值语句v=var。这个打印结果和前面的描述是一致的。在命令前提供变量赋值语句,命令执行时将变量添加到环境变量中。以及v=var的打印结果;环境|grepvar命令为空。该命令虽然在当前shell中定义了v变量,但并没有将v变量添加到当前shell的环境变量中。所以env打印不包含赋值语句v=var。验证“v=var./test.sh”和“v=var;./test.sh”命令的区别由于echo命令的代码本身并没有获取变量v的值,所以不明显v变量被添加到环境变量测试结果中。假设有一个包含以下内容的test.sh脚本:#!/bin/bashecho$v该脚本打印v变量的值。但是脚本本身并没有定义v变量。使用该脚本的测试结果如下:$v=var./test.shvar$v=var;./test.sh可以看到v=var./test.sh命令打印出了v变量对应的值。test.sh脚本本身虽然没有定义v变量,但是在执行过程中在命令名前提供了v=var变量赋值语句。这会在执行test.sh脚本时将v变量添加到环境变量中,让test.sh脚本获取到v变量的值。而v=var;./test.sh命令打印为空。这种写法是在当前shell中定义v变量。根据上面描述的“子shell中环境变量的继承关系”可以看出,这个v变量是不会被子shell继承的。所以在执行test.sh脚本时,并不会获取到父shell中v变量的值。而test.sh脚本本身并没有定义v变量,所以打印结果为空。最后修改test.sh脚本为如下内容,让脚本自己定义v变量:#!/bin/bashv=initecho$v再次测试结果如下:$v=var./test.shinit$v=变量;./test.shinit可以看到test.sh脚本自己定义v变量时,以test.sh脚本定义的值为准,不受环境变量的影响。结论一般情况下,当在命令名前提供变量赋值语句,且变量赋值语句与命令名之间用空格分隔时,执行命令时会将给定的变量赋值语句添加到环境变量中不影响当前shell的执行环境。这篇文章的出发点是从偶然看到一个WINEPREFIX=/home/.no1-winewine/home/.no1-wine/yyyy命令开始的,敏锐地意识到了这个写法的古怪。我没有轻易放弃这个问题。通过查看GNUbash的联机帮助手册,找到了与这种写法相对应的描述。
