变量和引用
1 变量
1.1 什么是变量
变量用于保存有用信息,如路径名、文件名、数字等,Linux用户使用变量定制其工作环境,使系统获知用户相关的配置。
变量可分为:本地变量、环境变量和位置参数。
(1) 本地变量是仅可以在用户当前Shell生命期的脚本中使用的变量,本地变量随着Shell进程的消亡而无效,本地变量在新启动的Shell中依旧无效,它类似于C、C++、Java等编程语言中局部变量的概念。本地变量中包含环境变量,Linux的本地变量的非环境变量不具备继承性。
(2) 环境变量则适用于所有由登录进程所产生的子进程,简言之,环境变量在用户登录后到注销之前的所有编辑器、脚本、程序和应用中都有效。新的环境变量通过export命令来定义。Linux的环境变量具有继承性,即子Shell会继承父Shell的环境变量。
(3) 位置参数也属于变量,它由于向Shell脚本传递参数,是只读的。
1.2 变量替换和赋值
Shell的三类变量中,位置参数是只读变量,因此,没有变量的替换和赋值操作,其他两类变量都存在上述操作。
变量是某个值的名称,引用变量值就称为变量替换,$ 符号是变量替换符号,如variable是变量名,那么,$variable或${variable}就表示变量的值。
将值赋给某个变量名就称为变量赋值,变量赋值有两种格式,如下所示:
variable=able ${ variable=value } |
对于变量赋值,有以下三点说明:
(1) 等号的两边可以有空格,这不影响操作;
(2) 如果值(value)中包含空格,则必须用双引号括起来;
(3) 变量名只能包括大小写字母(a~z和A~Z)、数字(0~9)、下滑杠(_)等符号,并且变量名不能以数字开头,否则视为无效变量名。
利用unset命令可以清除变量的值,命令格式为:unset 变量名
除了使用等号进行变量赋值以外,变量赋值还有下面几种模式:
模 式 |
意 义 |
variable=value |
将值赋给变量variable |
variable+value |
对已赋值的variable,重设其值 |
variable?value或variable:?value |
对未赋值的variable,显示系统错误信息 |
variable:=value |
对未赋值的variable,将值value值赋给它 |
variable:-value |
对未赋值的variable,将值value值赋给它,但value值不存储到variable对应的地址空间 |
1.3 无类型的Shell脚本
在C、C++、Java等编程语言中,定义变量需要声明其类型,如×××、浮点型、字符型等。Shell脚本变量却是无类型的,这与awk变量是一样的。bash Shell不支持浮点型,只支持×××和字符型,默认情况下,Shell脚本变量是字符型的,同时,字符型的变量还具有一个整型值,为0,尽管如此,bash Shell并不要求在定义一个变量时声明其类型。但是,Shell会根据上下文判端出数值型的变量,并进行变量的算术运算和比较等数值操作。判定标准时变量中是否只包含数字,如果变量只包含数字,则Shell认定该变量是数字型的,反之,Shell认定该变量是字符串。
下面通过一个例子分析Shell对无类型变量的处理方式,新建一个名为integer.sh的脚本,内容如下:
#!/bin/bash
a=2009 let "a+=1" echo "a=$a"
b=xx09 echo "b=$b" declare -i b echo "b=$b"
let "b+=1" echo "b=$b"
exit 0 |
integer.sh的脚本首先定义a变量,值为2009,然后将a的值增加1,再输出a的值。从下面的integer.sh脚本执行结果可以看到,Shell自动将a解析为数值变量,a=2010。let命令用于在变量上执行算术运算,实际上let "a+=1"等价于a=+1。然后integer.sh脚本定义b变量,值为xx09,b显然是字符型的,我们利用declare命令将b强制转化为×××,发现b的值并没有改变,即declare强制转化并没有起作用。将b执行算术操作,增加1,结果为b=1,即字符型变量的数值为0。integer.sh脚本的执行结果如下所示:
[root@localhost ~]# . integer.sh a=2010 b=xx09 b=xx09 b=1 |
接下来,我们通过一个例子说明Shell对空字符串和未定义变量的处理方式,新建null-undeclare.sh脚本,内容如下:
#!/bin/bash
c="" echo "c=$c" let "c+=1" echo "c=$c"
echo "e=$e" let "e+=1" echo "e=$e"
exit 0 |
null-undeclare.sh脚本定义c变量为空字符串,将c执行算术操作,增加1,结果为c=1,即空字符串变量的数值仍为0。然后null-undeclare.sh脚本直接输出未曾预先定义的变量e,结果e为空值,将e加1,结果为e=1。null-undeclare.sh脚本的执行结果如下所示:
[root@localhost ~]$ . null-undeclar.sh c= c=1 e= e=1 |
从以上两个脚本不然总结出:Shell脚本变量是无类型的,并且Shell变量同时有数值型和字符型两种赋值,数值型的初值为0,字符型的初值为空,而且可以不预先定义变量而直接使用它。
1.4 环境变量
1.4.1 定义和清除环境变量
环境变量的替换和赋值依然遵循前面变量所述的一般规律,环境变量也是无类型的,环境变量的特殊之处仅在于它的值适用于所有由登录进程所产生的子进程。定义环境变量的格式是:
#定义环境变量的基本格式 ENVIRON-VARIABLE = value #环境变量赋值 export ENVIRON-VARIABLE #声明环境变量 |
在给环境变量赋值后,用export命令声明一下,就说明此变量为环境变量,环境变量的名称一般由大写字母组成。
如果要列出系统中所有的环境变量,可使用env命令或printenv命令。
清除环境变量值的方法与清除其他变量的方法一样,都是用unset命令。
1.4.2 查看环境变量
(1) 使用echo命令查看单个环境变量。
例如:echo $PATH
(2) 使用env或printenv命令查看所有环境变量。
例如:env
(3) 使用set或declare查看所有本地定义的环境变量。
例如:set
declare
1.4.3 环境变量文件
在shell的命令行下直接定义的变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。
如果只是临时用一下,可以直接在shell下用export命令设定环境变量;如果希望此环境变量每次开机或打开Shell时自动设定而无须每次都手动设定,那么需要将export命令写入环境变量配置文件中。Linux下,环境变量被分别保存在多个环境变量文件中,当你进入系统的时候,这些环境变量文件是按照下面的顺序读取的:
(1)/etc/environment文件
设置整个系统的环境信息,在系统启动时被读取,与登录用户无关。
(2)/etc/profile文件
为系统的每个用户设置环境信息,任何用户每次登录时执行该文件。
(3)/etc/bashrc文件
是针对所有用户的bash Shell初始化文件,任何用户每次登录或打开新的Shell时执行该文件。在执行完/etc/profile 内容之后,如果用户的Shell运行的是bash shell,那么接着就会执行此文件。
(4) ~/.bashrc文件
该文件包含专用于你的bash shell的bash信息,在用户每次登录时或打开新的Shell时执行该文件。
(5)~/.bash_profile文件
每个用户都可使用该文件保存专用于自己使用的shell信息,在用户每次登录时执行该文件。
注意:在不同Linux中,这个文件的名字不同,可能的名字有~/.bash_profile、~/.bash_login、~/.profile其中的一种或者几种。如果都存在的话,执行的顺序为~/.bash_profile、~/.bash_login、~/.profile。
(6) ~/.bash_logout文件
在用户每次退出登录的时候执行该文件。
/etc/environment文件、/etc/profile文件、/etc/bashrc文件是针对所有用户的。
~/.bashrc文件、~/.bash_profile文件、~/bash_logout文件在每个用户的主目录(HOME目录)下都存在,是针对特定用户的(哪个用户的主目录就针对哪个用户)。
例如,编辑/etc/profile文件,添加CLASSPATH变量:
# vi /etc/profile
添加内容如下:
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib
注:修改文件后要想马上生效还要运行#source /etc/profile,不然只能在下次重进此用户时生效。
1.4.4 重要的环境变量
(1) BASH 和SHELL
BASH记录了bash Shell的路径,通常为/bin/bash,内部变量SHELL就是通过BASH的值确定当前Shell的类型。
SHELL变量保存默认的Shell值,默认的值为/bin/bash,表示当前的Shell是bash Shell。如果有必要用另一个Shell,则需要重置SHELL变量值。
下面显示了BASH和SHELL变量的值,由于Red Hat使用的是bash Shell,因而两个变量的值都是/bin/bash。
[root@localhost ~]# echo $BASH /bin/bash [root@localhost ~]# echo $SHELL /bin/bash |
(2) BASH_VERSION和BASH_VERSINFO[n]
BASH_VERSION表示安装在系统上的Bash版本号。
BASH_VERSINFO[n]是一个含有6个元素的数组, 它包含了所安装的Bash的版本信息,与下$BASH_VERSION很相像。
(3) SHLVL
SHLVL记录了bash Shell嵌套的层次,一般来说,我们启动第一个Shell时,SHLVL为1,如果在这个Shell中执行脚本,脚本中的SHLVL为2,如果脚本再执行子脚本,子脚本中的SHLVL就变为3。
(4)HOSTTYPE、MACHTYPE、OSTYPE
HOSTTYPE和MACHTYPE一样,都用于记录系统的硬件架构。
OSTYPE记录了操作系统类型。
(5) PATH
PATH是Linux中一个极为重要的环境变量,它用于帮助Shell找到用户所输入的命令。用户所输入的每个命令实际是是一个源代码文件,计算机执行这个文件里的代码以实现这个命令的功能,这些源代码文件称为可执行文件。可执行文件存在于各种各样的目录下,一些可执行文件放置在Linux系统标准目录中,还有一些可执行文件可能是用户写得的序或Shell脚本文件,他们放置在用户目录中。PATH就记录了一系列的目录列表,Shell为每个输入命令搜索PATH中的目录列表。
下面显示了PATH环境变量的内容,可以看到,PATH中包含了多个目录的路径,它们之间用冒号分割,都是以bin或sbin的文件夹结尾,bin或sbin是Linux中存放可执行文件的文件夹。任何在PATH的可执行文件都可以在Linux系统的任何目录中直接执行。
[root@bogon ~]# echo $PATH /usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin |
如果需要在PATH中添加新目录,可以使用以下的命令:
export PATH="/new directory":$PATH |
命令中的new directory就是新加上去的目录,后面用冒号加$PATH,表示new directory加上旧的PATH变量值,得到新的PATH变量值。
(6)HOME
HOME记录当前用户的根目录。root用户的根目录时/root,其他用户的根目录都存放在/home目录下,通常以用户名命名。
(7)PWD和OLDPWD
PWD记录当前的目录路径,当利用cd命令改变当前目录时,系统自动更新PWD的值,OLDPWD记录旧的工作目录,即用户所处的前一个目录。
(8)USER、UID、LOGNAME
USER、UID、LOGNAME是保存用户信息的环境变量,USER表示当前登录用户的账户,UID表示当前登录用户的ID、LOGNAME表示当前登录用户的登录名。
(9)PPID
PPID是创建当前进程的进程号,即当前进程的父进程。
(10)PS1、PS2、PS3、PS4
PS1:主提示符,在命令行中可以见到它。默认显示“[\u@\h\W]$”。
PS2:第二提示符,当你需要额外输入的时候,你就会看到他。默认显示“>”。当命令比较长,需要多行输入时,在当行命令的末尾输入字符“\”,会出现下一行命令输入行,这时的提示符为第二提示符。
PS3:第三提示符,它在select循环中显示。默认显示“#?”。
PS4:第四提示符,当使用set -x选项来设置Shell的执行方式时,这个提示符会出现在每行输出的开头。默认显示“+”。
PS1、PS2、PS3、PS4是在用户根目录下的.bash_profile中定义的。可以更改这些提示符的显示样式。
下面列出了提示符中特殊字符的意义:
\d:代表日期,格式为weekday month date,例如:"Mon Aug 1" \H:完整的主机名称。例如:我的机器名称为:fc4.linux,则这个名称就是fc4.linux \h:仅取主机的第一个名字,如上例,则为fc4,.linux则被省略 \t:显示时间为24小时格式,如:HH:MM:SS \T:显示时间为12小时格式 \A:显示时间为24小时格式:HH:MM \u:当前用户的账号名称 \v:BASH的版本信息 \w:完整的工作目录名称。家目录会以~代替 \W:利用basename取得工作目录名称,所以只会列出最后一个目录 \#:下达的第几个命令 \$:提示字符,如果是root时,提示符为:# ,普通用户则为:$ |
(11)IFS
IFS用于指定Shell域分隔符,默认为空格。这个变量用来觉得Bash在解释字符串时如何识别域,或者单词边界。
下例中,重新设置IFS变量的值,它将IFS设定为冒号,然后显示PATH的值,Shell就以冒号为分隔符,将PATH的各目录以空格分割而显示出来。
[root@localhost ~]# export IFS=: [root@localhost ~]# echo $PATH /usr/lib/qt-3.3/bin /usr/local/sbin /usr/local/bin /sbin /bin /usr/sbin /usr/bin/ root/bin |
注意,脚本内部修改IFS只在脚本内部起作用,当前Shell修改IFS只在当前Shell及子Shell中有效。
(12)MAIL
MAIL表示当前用户的邮件存放目录。
(13)HOSTNAME
HOSTNAME记录了主机名,Linux主机名是网络配置时必须要设置的参数,我们一般在/etc/sysconfig/network文件中设置主机名。/etc/hosts文件用于设定IP地址和主机名之间的对应关系,以利于快速从主机名查找到IP地址。
(14) LANG
系统字符集。
(15) SECONDS
SECONDS记录脚本从开始执行到结束所耗费的时间,以秒为单位。
(16)TMOUT
TMOUT变量用于设置Shell的过期时间,当TMOUT不为0时,Shell在TMOUT秒后将自动注销。TMOUT放在脚本中,可以规定脚本的执行时间。
(17) REPLY
REPLY变量与read和select命令有关。
read命令用于读取标准输入的变量值。在此,可以将read命令简单理解为接受用户的键盘输入,交互式Shell脚本经常用到read命令。read命令其一般格式为:
read variable |
variable是变量名,read将读到的标准输入存储到variable变量中,read命令也可以不带任何变量名,此时,read就将读到的标准输入存储到REPLY变量中。新建脚本readreply.sh,内容如下:
# readreply.sh脚本 #!/bin/bash
echo -n "What is your name?" read echo "Your name is $REPLY"
echo -n "What is the name of your father?" read fname echo "Your father's name is $fname" |
readreply.sh脚本的执行结果如下:
[root@bogon ~]# . readreply.sh What is your name?lcb Your name is lcb What is the name of your father?lqq Your father's name is lqq |
bash Shell的select命令源自于Korn Shell,是一种建立菜单的工具,它提供一组字符串供用户选择,用户不必完整地输入字符串,只需输入相应的序号进行选择,select命令的格式如下:
select variable in list do Shell命令1 Shell命令2 Shell命令3 ...... break done |
上述select命令格式中的list是字符串列表,select自动将list形成有编号的菜单,用户输入序号以后,将该序号所对应的list中的字符串赋给variable变量,而序号值则保存到REPLY变量中。select命令的dobreak语句段中可以添加Shell命令,对variable或REPLY进行调用。
下例阐述select命令的用法,以及REPLY变量在select命令中的含义。新建一个名为selectreply.sh的脚本,内容如下:
#!/bin/bash
echo "Pls. choose your profession?" select var in "Worker" "Doctor" "Teacher" "Student" "Other" do echo "The \$REPLY is $REPLY." echo "Your profession is $var." break done |
执行结果如下:
[root@bogon ~]# . selectreply.sh Pls. choose your profession? 1) Worker 2) Doctor 3) Teacher 4) Student 5) Other #? 4 The $REPLY is 4. Your proffession is Student. |
在上述selectreply.sh脚本的执行结果中,我们注意到输入select命令后,Shell提示符变为"#?",该提示符由Shell提示符变量PS3进行设置,“#?”是其默认值,可以重新设置。
1.5 位置参数
位置参数(positional parameters)是一种特殊的Shell变量,用于从命令行向Shell脚本传递参数,$1表示第1个参数、$2表示第2个参数等,$0为脚本的名字,从${10}开始,参数号需要用花括号起来,如${10}、${11}、${100}、...。$*和$@一样,表示从$1开始的全部参数。
Shell还定义了一些特殊的位置参数:
特殊位置参数 |
意义 |
$# |
传递到脚本的参数数量 |
$*和$@ |
传递到脚本的所有参数 |
$$ |
脚本运行的进程号 |
$? |
命令的退出状态,0表示没有错误,非0表示有错误 |
下面举例说明未知参数的用法,新建一个名为position的脚本,内容如下:
#!/bin/sh echo "The script name is: $0" echo "Parameter #1: $1" echo "Parameter #2: $2" echo "Parameter #3: $3" echo "Parameter #4: $4" echo "Parameter #5: $5" echo "Parameter #6: $6" echo "Parameter #7: $7" echo "Parameter #8: $8" echo "Parameter #9: $9" echo "Parameter #10: ${10}" echo "--------------------------" echo "All the command line parameters are: $*" |
position脚本接受命令行的10个参数,然后将$0、$1、$2、...、${10}、$*回显出来。下面给出position脚本的执行结果,从中可以清晰地看出$0、$1、$2、...、${10}、$*等符号的意义:
[root@localhost ~]# . position a bat cat dive eager fair gate hi ideal java The script name is: position Parameter #1: a Parameter #2: bat Parameter #3: cat Parameter #4: dive Parameter #5: eager Parameter #6: fair Parameter #7: gate Parameter #8: hi Parameter #9: ideal Parameter #10: java -------------------------- All the command line parameters are: a bat cat dive eager fair gate hi ideal java |
1.6 变量操作相关命令
1.6.1 source命令
source命令主要用于运行脚本文件。
source命令是bash shell的内置命令。source命令也称为“点命令”,也就是一个点符号(.);点命令,就是个点符号(.),是source的另一名称。
该命令的一般格式为:source FileName 或 . FileName
1.6.2 echo命令
echo命令的功能是在显示器上显示一段文字,一般起到一个提示的作用。
该命令的一般格式为: echo [参数] 字符串
字符串能加引号,也能不加引号。
1.6.3 readonly命令
readonly命令将变量设置为只读变量。如果使用了readonly命令的话,变量就不可以被修改或清除了。
该命令的一般格式为: readonly variable
示例:
$ export TEST="Test..." # 增加一个环境变量TEST $ readonly TEST # 将环境变量TEST设为只读 $ unset TEST # 会发现此变量不能被删除 -bash: unset: TEST: cannot unset: readonly variable $ TEST="New" # 会发现此也变量不能被修改 -bash: TEST: readonly variable |
只执行readonly命令,可以查看系统中所有的只读变量。
当然,用readonly设置变量为只读只是其中的一种方法,我们还可以利用declare和typeset命令实现同样的功能。
1.6.4 set命令和unset命令
set命令可以设置各种Shell选项或者显示当前本地变量。
该命令的一般格式:set [参数]
-a 标示已修改的变量,以供输出至环境变量。 -b 使被中止的后台程序立刻回报执行状态。 -C 转向所产生的文件无法覆盖已存在的文件。 -d Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。 -e 若指令传回值不等于0,则立即退出shell。 -f 取消使用通配符。 -h 自动记录函数的所在位置。 -H Shell 可利用"!"加<指令编号>的方式来执行history中记录的指令。 -k 指令所给的参数都会被视为此指令的环境变量。 -l 记录for循环的变量名称。 -m 使用监视模式。 -n 只读取指令,而不实际执行。 -p 启动优先顺序模式。 -P 启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。 -t 执行完随后的指令,即退出shell。 -u 当执行时使用到未定义过的变量,则显示错误信息。 -v 显示shell所读取的输入值。 -x 执行指令后,会先显示该指令及所下的参数。 |
不带任何参数的set命令将显示当前环境的所有的变量,包括环境变量和一些非环境变量。
unset命令用来清除变量的值,不管这个变量是环境变量还是本地变量。
该命令的一般格式是:unset变量
1.6.5 export命令
export命令用来设置环境变量。
该命令的一般格式是:export [-fnp] [变量名称]=[变量设置值]
-f 代表[变量名称]中为函数名称。
-n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
-p 列出所有的shell赋予程序的环境变量。
1.6.6 env命令和printenv命令
env命令和printenv命令用来显示用户的环境变量。
2 引用
2.1 概念
引用指将字符串用引用符号引起来,以防止特殊字符被Shell脚本重解释为其他意义。
特殊字符是指除了字面意思之外还可以解释为其他意思的字符,如$符号的字面意思就是美圆符号,但是$还表示正则表达式中的行尾,还可进行变量替换。
Shell中定义了四种引用符号:
符号 |
名称 |
意义 |
" " |
双引号 |
引用除美元符号($)、反引号(`)和反斜线之外的所有字符 |
' ' |
单引号 |
引用所有的字符 |
` ` |
反引号 |
Shell将反引号中的内容解释为系统命令 |
\ |
反斜线 |
转义符,屏蔽下一个字符的特殊意义 |
2.2 全引用和部分引用
双引号引用除美圆符号($)、反引号(`)和反斜线(\)之外的所有字符,即($)、(`)和(\)在双引号中仍被解释为特殊意义。利用双引号引用变量能够防止字符串分割,保留变量中的空格。
新建一个名为double.sh的脚本,内容如下:
#!/bin/bash variable1=2010 echo "$variable1" echo $variable1
variable2="x y z" #字符之间用多个空格分开 echo "$variable2" echo $variable2 |
double.sh脚本定义两个变量:variable1为数字2010,variable2为字符串,字符之间用多个空格分开,对这两个变量分别打印$ variable和“$variable”的值。下面给出double.sh脚本的执行结果,可以看到对于变量值是数字的情况,$ variable1和"$variable1"的相同;对于由多个空格分开的字符串的情况,"$variable2"打印出了保留多个空格的字符串,而$ variable2打印出的字符只有一个空格相分割。
单引号引用了所有的字符,即单引号中字符除单引号本身之外都解释为字面意义,单引号不再具备引用变量的功能。因此,我们通常将单引号的引用方式称为全引用,将双引号的引用方式称为部分引用。
2.3 命令替换
命令替换是指将命令的标准输出作为值赋给某个变量,baseShell定义了两种语法进行命令替换,一种是使用反引号,另一种是利用$(),两种等价的语法格式如下:
`Linux 命令` $(Linux 命令) |
比如,pwd是显示当前工作目录的命令,`pwd`和$(pwd)等价,值都为当前工作目录,与环境变量$PWD的值一样。
2.4 转义
反斜线符号(\)表示转义,当反斜线后面的一个字符具有特殊意义时,反斜线将屏蔽下一个字符的特殊意义,而以字面意义解析它。特殊字符即包括正则表达式中定义的元字符,也包括Shell重定向、管道命令中的一些特殊字符。
转义字符除了拼版 特殊字符的特殊意义之外,在echo、sed和awk等命令中,转义符加上一些特殊字母能够表达特殊的:
符 号 |
意 义 |
\n |
新的一行 |
\r |
返回 |
\t |
表示Tab键 |
\v或\f |
换行但光标仍就停留在原来的位置 |
\b |
退格键(Backspae) |
\a |
发出警报声 |
\0xx |
ASCII码所对应的字符 |
3 变量的高级用法
参考Linux编程从入门到精通。