1.3 Linux Shell的变量
你一定想知道,Shell是如何记忆工作目录的改变的?当Shell接收到外部命令时,在庞大的文件系统中,如何迅速定位到命令文件呢?如何设定Linux Shell的环境变量?如何使黑乎乎的命令行看起来更漂亮?这些问题都是本节要解决的问题。
1.3.1 变量
变量(variable)在许多程序设计语言中都有定义,与变量相伴的有使用范围的定义。Linux Shell也不例外。变量,本质上就是一个键值对。例如,str=“hello”,就是将字符串值(value)“hello”赋予键(key)str。在str的使用范围内,我们都可以用str来引用“hello”值,这个操作叫做变量替换。
Shell变量的名称以一个字母或下划线符号开始,后面可以接任意长度的字母、数字或下划线。和许多其他程序设计语言不同的是,Shell变量名称字符并没有长度限制。Linux Shell并不对变量区分类型。一切值都是字符串,并且和变量名一样,值并没有字符长度限制。神奇的是,bash也允许比较操作和整数操作。其中关键因素是:变量中的字符串值是否为数字。例如1.5所示。
例1.5 Linux Shell中的变量
alloy@ubuntu:~/Linux Shell/ch1$ long_str="Linux_Shell_programming"
alloy@ubuntu:~/Linux Shell/ch1$ echo $long_str
Linux_Shell_programming
alloy@ubuntu:~/Linux Shell/ch1$ add_1=100
alloy@ubuntu:~/Linux Shell/ch1$ add_2=200
alloy@ubuntu:~/Linux Shell/ch1$ echo $(($add_1+$add_2))
300
由例1.5可见,虽然Linux Shell中的变量都是字符串类型的,但是同样可以执行比较操作和整数操作,只要变量字符串值是数字。
变量赋值的方式为:变量名称=值,其中“=”两边不要有任何空格。当你想使用变量名称来获得值时,在名称前加上“$”。例如,$long_str。当赋值的内容包含空格时,请加引号,例如:
alloy@ubuntu:~/Linux Shell/ch1$ with_space="this contains spaces."
alloy@ubuntu:~/Linux Shell/ch1$ echo $with_space
This contains spaces
注意:$with_space事实上只是${with_space}的简写形式,在某些上下文中$with_space可能会引起错误, 这时候你就需要用${with_space}了。
当变量“裸体”出现的时候(没有$前缀的时候),变量可能存在如下几种情况:变量被声明或被赋值;变量被unset;或者变量被export。
变量赋值可以使用“=”(比如var=27), 也可以在read命令中或者循环头进行赋值,例如,for var2 in 1 2 3。
被一对双引号("")括起来的变量替换是不会被阻止的。所以双引号被称为部分引用,有时候又被称为“弱引用”。但是如果使用单引号的话(' '),那么变量替换就会被禁止了,变量名只会被解释成字面的意思,不会发生变量替换。所以单引号被称为“全引用”,有时候也被称为“强引用”。例如:
alloy@ubuntu:~/LinuxShell/ch1$var=123
alloy@ubuntu:~/LinuxShell/ch1$ echo '$var' #此处是单引号
alloy@ubuntu:~/LinuxShell/ch1$ echo "$var" #此处是双引号
$var
123
在这个例子中,单引号中的$var没有替换成变量值123,也就是说,变量替换被禁止了;而双引号中的$var发生了变量替换。即:单引号为全引用(强应用),双引号为弱引用。
在Shell的世界里,变量值可以是空值(“NULL”值),就是不包含任何字符。这种情况很常见,并且也是合理的。但是在算术操作中,这个未初始化的变量常常看起来是 0。但是这是一个未文档化(并且可能是不可移植)的行为。例如:
alloy@ubuntu:~/LinuxShell/ch1$ echo "$uninit" #未初始化变量
#此行为空,没有输出
alloy@ubuntu:~/LinuxShell/ch1$ let "uninit+=5" #未初始化变量加5
alloy@ubuntu:~/LinuxShell/ch1$ echo "$uninit"
5 #此行输出结果为5
alloy@ubuntu:~/LinuxShell/ch1$
Linux Shell中的变量类型有两种:局部变量和全局变量。
顾名思义,局部变量的可见范围是代码块或函数中。这一点与大部分编程语言是相同的。
但是,局部变量必须明确以local声明,否则即使在代码块中,它也是全局可见的。
环境变量是全局变量的一种。全局变量在全局范围内可见,在声明全局变量时,不需要加任何修饰词。
例1.6 测试全局变量和局部变量的适用范围
1 #! /bin/sh
2 # 测试全局变量和局部变量的适用范围
3 num=123
4 func1 ()
5 {
6 num=321 #在代码块中声明的变量
7 echo $num
8 }
9 func2()
10 {
11 local num=456 #声明为局部变量
12 echo $num
13 }
14 echo $num #显示初始时的num变量
15 func1 #调用func1,在函数体中赋值(声明?)变量
16 echo $num #测试num变量是否被改变
17 func2 #调用func2,显式声明局部变量
18 echo $num #测试num变量是否被改变
我们看看例1.5的运行结果:
123 #初始值
321 #func1内被改变
321 #func1内的赋值影响到函数体外
456 #func2内声明局部变量
321 #函数体外的num未改变
例1.6的解释如下。
我们设置了一个变量num,初始值赋值为123。
调用func1,func1中的赋值命令num=321将num的123覆盖。注意,此处虽然位于函数体内,但是还是能够修改全局变量,此处的num变量就是全局环境中的num。
调用func2,func2中定义了局部(local)变量num,并且赋值456。在func2内部,num变量的值为456,此时为局部的;当func2返回后,回到全局作用区,此时num的值并未改变,为321。
1.3.2 用echo输出变量
例1.7:
alloy@ubuntu:~/Linux Shell/ch1$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.3/bin
alloy@ubuntu:~/Linux Shell/ch1$ echo "hello world!"
Hello world!
在例1.7中,我们展示了echo的使用。echo命令的任务就是输出一行文本。多用于提示用户或产生数据。
我们将在echo的manpage中显示更多选项。
echo
echo [OPTION]... [STRING]...
语法:
描述:
允许在标准输出上显示STRING(s)。
主要选项:
-n不输出行尾的换行符。
行为模式:
echo将各个参数打印到标准输出。参数间以一个空格隔开,在输出结束后,换行。它会解释每个字符串里的转义序列(escape sequences)。转义序列可以用来表示特殊字符,以及控制其行为模式。
警告:
echo命令的-n选项并不被所有Linux版本支持。POSIX标准中并未包含此选项。
转义字符可以表示程序中难以看得见或者难以输入的特殊字符。当echo遇到转义序列时,就会打印相应的字符。echo支持的转义字符如表1-1所示。
表1-1 echo的转义字符序列
1.3.3 环境变量的相关操作
在通常情况下,每个进程都有自己的“环境”,这个环境是由一组变量组成的,这些变量中存有进程可能需要引用的信息。在这种情况下,Shell与一般的进程没什么区别。
每次当一个Shell启动时,它都将创建适合于自己环境变量的Shell变量。更新或者添加一个新的环境变量的话,这个Shell都会立刻更新它自己的环境(换句话说,更改或增加的变量会立即生效),并且所有后继生成的Shell子进程(即这个Shell所执行的命令)都会继承这个环境。
如果一个脚本要设置一个环境变量,那么需要将这些变量“export”出来,也就是需要通知到脚本本地的环境。这是export命令的功能。
一个脚本只能够export变量到这个脚本所产生的子进程,也就是说只能够对这个脚本所产生的命令和进程起作用。如果脚本是从命令行中调用的,那么这个脚本所export的变量是不能影响命令行环境的。也就是说,子进程是不能够export变量来影响产生自己的父进程的环境的。但是,当使用source命令执行脚本时,因为没有子进程的产生,此时脚本中的export命令将会影响父进程的环境。
export
语法:
export [-fnp][变量名称]=[变量设置值]。
描述:
export命令用于设置或显示环境变量。
主要选项:
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的Shell赋予程序的环境变量。
行为模式:
export命令修改当前Shell进程的环境变量。若将export命令置于脚本中被调用执行,则export命令对父Shell进程的环境变量没有影响。
警告:
Shell中执行程序时,Shell会提供一组环境变量。export可新增,修改或删除环境变量,供后续执行的程序使用。export的效力仅及于该此登录操作。
export命令用于设置当前进程的环境变量。但是有效期仅维持到当前进程消亡为止。下次重新登录到命令行Shell时,以前对Shell的export设置都无法恢复。如果想要把对环境变量的设置永久保存,则可以将export命令置于Shell登录时执行的启动文件中。例如:
# 设置环境变量PATH
export PATH=/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin
启动文件包含别名和环境变量,正是这些别名和环境变量才使得Shell可以作为一个用户Shell来运行。当系统初始化之后,这些别名和变量也可被其他的Shell脚本调用。
对于从bash来说,启动文件在表1-2中列出。
表1-2 bash的启动文件/登出文件
① 参见Shell的发展史
注意,此处的$HOME为环境变量,$HOME变量的值是登录者的用户目录。$HOME目录下存放有许多用户个人相关的文件和数据,还有对用户定制的配置文件。这些配置文件往往以“.”开头(隐藏文件)。例如:
alloy@ubuntu:~/Linux Shell/ch1$ echo $HOME #显示环境变量HOME
/home/alloy
对于其他Shell的启动文件,请参阅相关章节。
export命令设置适用于当前Shell的环境变量值。修改后维持不变,直到当前Shell消亡。env命令则可以临时改变环境变量值。
alloy@ubuntu:~/Linux Shell/ch1$ env–i PATH=./:$PATH echo.sh
“-i”选项使Shell在执行echo.sh时,清空所有由父Shell继承来的环境变量,仅仅设置命令中指定的PATH变量(将“./”也添加到命令搜寻路径里)。这样,在执行echo.sh时,就不需要给出完全路径(./echo.sh),直接给出命令文件名,系统就知道在哪里找该命令了。
unset命令从当前Shell中删除函数或变量。删除变量时,使用“-v”选项(默认情况),删除函数时,使用“-f”选项。例如:
alloy@ubuntu:~/Linux Shell/ch1$ echo $vari
123
alloy@ubuntu:~/LinuxShell/ch1$ unset vari #删除变量(unset–v vari)
alloy@ubuntu:~/LinuxShell/ch1$ echo $vari
#此行为空,因为vari为空
alloy@ubuntu:~/LinuxShell/ch1$ hello() {echo“hello, world!”} #定义函数
alloy@ubuntu:~/LinuxShell/ch1$ unset–f hello #删除函数
env
语法:
env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
描述:
在重建的环境中运行程序,设置环境中的每个NAME为VALUE,并且运行COMMAND。
主要选项:
-i,--ignore-environment
不带环境变量启动
-u,--unset=NAME
从环境变量中删除一个变量
行为模式:
未提供COMMAND时,显示环境中所有变量的名称和值。提供COMMAND时,根据参数重建环境变量后,在新的环境中运行COMMAND。
unset
语法:
unset [-v] variable…
unset–f function…
描述:
从当前Shell删除变量或函数。
主要选项:
-f
删除指定的函数:
-v
删除指定的变量。在没有提供任何选项的情况下,默认此选项。
行为模式:
如果没有提供任何选项,则默认unset为删除变量(-v选项)。如果使用-f选项,则被视为删除函数操作,参数为函数名称。
NOTE:
Env函数和set函数不同。Env函数显示的是环境变量,而set函数则显示所有的本地变量,包括用户的环境变量。例如,当用户在命令行中设置var=123时,set函数将显示var变量,而env函数则不显示(var此时是本地变量,不是环境变量)。如果使用export var=123命令,则set命令和env命令都可以显示var变量。
1.3.4 Shell中一些常用环境变量
Linux 是一个多用户的操作系统。每个用户登录系统后,都会有一个专用的运行环境。通常用户默认的环境都是相同的,这个默认环境实际上就是一组环境变量的定义。用户可以对自己的运行环境进行定制,其方法就是修改相应的系统环境变量。
表1-3列出了一些常见的环境变量。
表1-3 常见环境变量
在这些变量中,PATH变量中存储有一系列路径,路径中以冒号(:)分隔开。格式例如:
/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin
一般来说,PATH路径中至少包含/bin和/usr/bin两个目录。大部分时候还有/usr/X11R6/bin等X相关的目录。当Shell接收到一个命令,并且这个命令非内建命令,也没有给出完整路径时,Shell则在PATH变量中依次从左到右搜索目录,直到找到该命令为止。如果一个命令在PATH中两个不同目录下都存在,则位于PATH前端的目录中的命令会被执行。
PS1/PS2变量可以改变Shell的提示符。默认情况下,普通用户的提示符是“$”,root用户的提示符是“#”。可以通过修改这两个变量让Shell的交互界面更友善。