13.10 变量
13.10.1 变量类型
变量可分为两类:局部变量和环境变量。局部变量只在创建它们的shell中可用。而环境变量则可以在创建它们的shell及其派生出来的任意子进程中使用。有些变量是用户创建的,其他的则是专用shell变量。
13.10.2 命名惯例
变量名必须以字母或下划线字符开头。其余的字符可以是字母、数字(0~9)或下划线字符。任何其他的字符都标志着变量名的终止。名字是大小写敏感的。给变量赋值时,等号周围不能有任何空白符。为了给变量赋空值,可以在等号后跟一个换行符。创建一个局部变量最简单的格式是给一个变量赋值,如以下格式所示。
格式
变量=值
范例13-49
name=Tommy
13.10.3 内置命令declare
有两个内置命令可以用来创建变量,它们是declare和typeset,其选项可以控制变量设置的方式。typeset命令(来自Korn shell)的功能和declare命令(bash)完全一样。bash文档中指出,“提供typeset命令是为了和Korn shell兼容。但是不建议使用它,而应使用内置命令declare” 12。因此从这一点来说,我们将使用declare命令(即使我们使用typeset也一样方便)。
不带任何参数时,declare将列出所有已设置的变量。通常只读变量不能被重新赋值或复位。如果只读变量是用declare创建的,那它们不可以被复位,但可以被重新赋值。整型变量也可以用declare赋值。
格式
declare 变量=值
范例13-50
declare name=Tommy
表13-13 declare选项
选 项
含 义
-a 13
将变量当作一个数组。即,分配元素
-f
列出函数的名称和定义
-F
只列出函数名
-i
将变量设为整型
-r
将变量设为只读
-x
将变量名输出到子shell中
13.10.4 局部变量和作用域
变量的作用域指变量在一个程序中的哪些地方可见。对于shell来说,局部变量的作用域被限定在创建它们的shell中。
给变量赋值时,等号前后不能有空白符。如果要给变量赋空值,可以在等号后跟一个换行符 。14
变量前的美元符用来提取存储在变量里的值。
local函数可以用来创建局部变量,但仅限于在函数内使用(请参见13.16.1节,“定义函数”)。
设置局部变量 局部变量可以通过简单地赋予它一个值或一个变量名来设置,或者如范例13-51所示用declare内置函数来设置。
范例13-51
1 $ round=world or declare round=world
$ echo $round
world
2 $ name=\"Peter Piper\"
$ echo $name
Peter Piper
3 $ x=
$ echo $x
4 $ file.bak=\"$HOME/junk\"
bash: file.bak=/home/jody/ellie/junk: not found
说明
1. 将局部变量round赋值为world。如果遇到变量名前面是个美元符的情况,shell就执行变量替换。该命令显示变量round的值(不要混淆提示符($)和用来执行变量替换的$符)。
2. 局部变量name被赋值为“Peter Piper”。必须用引号来保护空白符,这样,shell分析命令行时才不会将这个字符串看成是两个词。该命令显示变量name的值。
3. 这条命令没有给变量x赋值,因此x被赋值为空。结果显示一个空值,即空字符串。
4. 变量名中出现句点是非法的。变量名可以使用的字符只能是数字、字母和下划线。因此,shell尝试将这个字符串作为一条命令来执行。
范例13-52
1 $ echo $$
1313
2 $ round=world
$ echo $round
world
3 $ bash # Start a subshell
4 $ echo $$
1326
5 $ echo $round
6 $ exit # Exits this shell, returns to parent shell
7 $ echo $$
1313
8 $ echo $round
world
说明
1. 双美元符变量的值是当前shell的PID。本例中这个shell的PID是1313。
2. 将局部变量round赋值为字符串world,并打印该变量的值。
3. 另外启动一个bash shell,这个shell被称为子shell(subshell或child shell)。
4. 当前这个shell的PID时1326,其父shell的PID则是1313。
5. 局部变量round在这个shell中没有定义,因此打印了一个空行。
6. exit命令终止当前shell并返回父shell(也可以按Ctrl+D组合键退出当前shell)。
7. 返回到父shell。显示它的PID。
8. 显示变量round的值。它是这个shell的局部变量。
设置只读变量 只读变量是不能被重新定义或复位的特殊变量。但是,如果使用了declare函数,只读变量可以被重新定义,但不能被复位。
范例13-53
1 $ name=Tom
2 $ readonly name
$ echo $name
Tom
3 $ unset name
bash: unset: name: cannot unset: readonly variable
4 $ name=Joe
bash: name: readonly variable
5 $ declare -r city=\'Santa Clara\'
6 $ unset city
bash: unset: city: cannot unset: readonly variable
7 $ declare city=\'San Francisco\' # What happened here?
$ echo $city
San Francisco
说明
1. 局部变量name的值被设为Tom。
2. 将变量name设为只读。
3. 不能复位只读变量。
4. 不能重新定义只读变量。
5. declare内置命令给只读变量city赋值Santa Clara。当所赋值串中包含空白符时必须用引号。
6. 因其是只读变量,所以不能被复位。
7. 当只读变量是由declare命令创建时,它不可以被复位,但可以被重新赋值。
13.10.5 环境变量
环境变量可用在创建它们的shell和从该shell派生的任意子shell或进程中。它们通常被称为全局变量,以区别于局部变量。通常,环境变量应该大写。环境变量是已经用export内置命令导出的变量。
变量被创建时所处的shell被称为父shell。如果父shell又启动了一个shell,这个新的shell被称作子shell。环境变量将传递给从创建它们的shell里启动的任意子进程。它们从父亲传递给儿子再到孙子等,但是不可向其他方向传递。比如,一个子进程可以创建环境变量,但不能将它传回给它的父进程,只能传给它的子进程 15。有一些环境变量,比如HOME、LOGNAME、PATH和SHELL,在用户登录之前就已经被/bin/login程序设置好了。通常,环境变量定义并保存在用户主目录下的.bash_profile文件中。请参见表13-14中列出的环境变量。
表13-14 bash环境变量
变 量 名
含 义
_(下划线)
上一条命令的最后一个参数
BASH
展开为调用bash实例时使用的全路径名
BASH_ENV
和ENV一样,但只可在bash 2.0或更高版本中设置 16
BASH_VERSINFO
使用2.0以上版本的bash时,展开为版本信息 16
BASH_VERSION
展开为当前bash实例的版本号
CDPATH
cd命令的搜索路径。它是以冒号分隔的目录列表,shell通过它来搜索cd命令指定的目标目录。例如.:~:/usr
COLUMNS
设置该变量就给shell编辑模式和选择的命令定义了编辑窗口的宽度
DIRSTACK
在2.0或以上版本的bash中,代表目录栈的当前内容
EDITOR
内置编辑器emacs、gmacs或vi的路径名
ENV
每一个新的bash shell(包括脚本)启动时执行的环境文件。通常赋予这个变量的文件名是.bashrc。ENV的值被解释为路径名前,shell先要对其进行参量扩展,命令替换和算术扩展
EUID
展开为在shell启动时被初始化的当前用户的有效ID
FCEDIT
fc命令的默认编辑器名
FIGNORE
执行文件名补全时可忽略的以冒号分隔的后缀列表。以FIGNORE中任一项为后缀的文件名被从匹配的文件名列表中排除。例如值为.o:~
FORMAT
用来格式化在命令管道上的time关键字的输出
GLOBIGNORE
在文件名扩展(称为globbing)时被忽略的文件列表
GROUPS
当前用户所属的组
HISTCMD
当前命令的历史编号或在历史清单中的序号。如果HISTCMD被复位,即使它随后就会重置,也将失去它的特殊属性
HISTCONTROL
如果设置了ignorespace值,以一个空格符开头的行将不会进入历史清单。如果设置了ignoredups值,那和前一个历史行匹配的行不会进入。值ignoreboth结合了这两个选项。如果被复位,或设置成除了上面所说的任意其他值时,所有被解释器所读的行都将保存到历史清单中
HISTFILE
指定保存命令行历史的文件。默认值是~/.bash_history。如果被复位,交互式shell退出时将不保存命令行历史
(续表)
变 量 名
含 义
HISTFILESIZE
历史文件能包含的最大行数。当给这个变量赋值后,如果有必要,历史文件将被截尾,以使包含的行数不超过这个数。默认值是500
HISTSIZE
记录在命令行历史文件中的命令数。默认是500
HOME
主目录。未指定目录时,cd命令将转向该目录
HOSTFILE
包含一个格式和/etc/hosts一样的文件的名称,当shell需要补全一个主机名时将读取该文件。文件可以交互式更改。下一次试图补全主机名时,bash将新文件的内容添加到已经存在的数据库中
HOSTTYPE
自动设置正在运行bash的机器的类型。默认值是由系统决定的
IFS
内部字段分隔符,一般是空格符、制表符和换行符,用于由命令替换,循环结构中的表和读取的输入产生的词的字段划分
IGNOREEOF
控制shell接收到单独一个EOF字符作为输入时的行为。如果设置,它的值就是shell退出前在一个输入行的最前面键入的连续EOF字符的个数。如果变量存在但没有一个数字值,或没有值,那么默认值是10。如果它不存在,EOF意味着给shell的输入的终止。它只在交互式shell中有效
INPUTRC
readline启动文件的文件名,取代默认的~/.inputrc
LANG
用来为没有以LC_开头的变量明确选取的种类确定locale类
LC_ALL
忽略LANG和任何其他LC_变量的值
LC_COLLATE
确定对路径名扩展的结果进行排序时的整理顺序,以及匹配文件名与模式时的范围表达式,等价类和整理序列的行为
LC_MESSAGES
确定用于转换前面有一个$的双引号串的locale
LINENO
每次shell在一个脚本或函数中替换代表当前连续行号(从1开始)的十进制数时,都将引用该参数
MACHTYPE
包含一个描述正在运行bash的系统的串
MAIL
如果该参数被设置为某个邮件文件的名称,而MAILPATH未被设置,当邮件到达MAIL指定的文件时,shell会通知用户
MAIL_WARNING
如果设置了该变量,当bash发现用于检查邮件的文件在上次检查后又被访问了,将打印消息“The mail in [filename where mail is stored] has been read”
MAILCHECK
这个参数定义shell将隔多长时间(以秒为单位)检查一次由参数MAILPATH或MAILFILE指定的文件,看看是否有邮件到达。默认值是600秒(10分钟)。如果将它设为0,shell每次输出主提示符之前都会去检查邮件
MAILPATH
由冒号分隔的文件名列表。如果设置了这个参数,只要有邮件到达任何一个由它指定的文件,shell都会通知用户。每个文件名后面都可以跟一个百分号和一条消息,当文件修改时间发生变化时,shell会显示这条消息。默认的消息是:You have mail
(续表)
变 量 名
含 义
OLDPWD
前一个工作目录
OPTARG
上一个由getopts内置命令处理的选项参数的值
OPTERR
如果设置成1,显示来自getopts内置命令的错误信息
OPTIND
下一个由getopts内置命令处理的参数的序号
OSTYPE
自动设置成一个串,该串描述正在运行bash的操作系统。默认值由系统决定
PATH
命令搜索路径。一个由冒号分隔的目录列表,shell用它来搜索命令。默认路径由系统决定,并且由安装bash的管理员设置。一个普通值为
/usr/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin:
PIPESTATUS
一个数组,包含一列最近在管道执行的前台作业的进程退出状态值
PPID
父进程的进程ID
PROMPT_COMMAND
赋给这个变量的命令将在主提示符显示前执行
PS1
主提示符串,默认值是$
PS2
次提示符串,默认值是>
PS3
与select命令一起使用的选择提示符串,默认值是#?
PS4
当开启追踪时使用的调试提示符串,默认值是+。追踪可以用set –x开启
PWD
当前工作目录。由cd设置
RANDOM
每次引用该变量,就产生一个随机整数。随机数序列可以通过给RANDOM赋值来初始化。如果RANDOM被复位,即使随后再设置,它也将失去特定的属性
REPLY
当没有给read提供参数时设置
SECONDS
每次SECONDS被引用,将返回调用shell以来的秒数。如果给SECONDS赋一个值,以后引用返回的值将是赋值以来的秒数加上所赋的值。如果SECONDS被复位,即使随后再设置,它也将失去特定的属性
SHELL
当调用shell时,它扫描环境变量以寻找该名字。shell给PATH、PS1、PS2、MAILCHECK和IFS设置默认值。HOME和MAIL由login(1)设置
SHELLOPTS
包含一列开启的shell选项,比如braceexpand、hashall、monitor等
SHLVL
每启动一个bash实例时将其加1
TMOUT
设置退出前等待输入的秒数
UID
展开为当前用户的用户ID,在shell启动时初始化
设置环境变量 如果想设置环境变量,就要在给变量赋值之后或设置变量时使用export命令(参见表13-15)。带-x选项的declare内置命令也可完成同样的功能(输出变量时不要在变量名前面加$)。
表13-15 export命令和它的选项
选 项
值
--
标志着选项末尾,余下的参数被视为变量
-f
名-值对被看作函数,而不是变量
-n
将一个全局(导出)变量转换成局部变量。之后该变量将不能被导出到子进程中
-p
显示所有的全局变量
格式
export变量=值
变量=值; export变量
declare –x 变量=值
范例13-54
export NAME=john
PS1= \'/d:/W:$USER> \' ; export PS1
declare -x TERM=sun
范例13-55
1 $ export TERM=sun # or declare -x TERM=sun
2 $ NAME=\"John Smith\"
$ export NAME
$ echo $NAME
John Smith
3 $ echo $$
319 # pid number for parent shell
4 $ bash # Start a subshell
5 $ echo $$
340 # pid number for new shell
6 $ echo $NAME
John Smith
7 $ declare -x NAME=\"April Jenner\"
$ echo $NAME
April Jenner
8 $ exit # Exit the subshell and go back to parent shell
9 $ echo $$
319 # pid number for parent shell
10 $ echo $NAME
John Smith
说明
1. 给TERM变量赋值sun。同时输出它。现在,由这个shell启动的所有进程都将继承这个变量。也可以用declare –x来完成同样的功能。
2. 定义并输出变量NAME,让它可以被当前shell启动的所有子shell使用。
3. 打印当前shell的PID的值。
4. 启动一个新的bash。这个新shell称为子shell。原来那个shell称为父shell。
5. 新的bash shell的PID保存在变量$$中,回显这个变量的值。
6. 在父shell中设置的变量NAME导出给这个新shell,这条命令显示它的值。
7. 内置的declare函数是设置变量的另一种方式。用-x开关,declare可以输出变量。该变量被重新设置为April Jenner。这个变化将输出到所有的子shell,但不会影响父shell。输出的变量不会向上传递给父shell。
8. 退出这个bash子shell。
9. 再次显示父shell的PID。
10. 变量NANE的值还跟原来一样。从父shell输出到子shell时,变量保持它们的值不变。子shell不可能改变父shell的变量的值。
13.10.6 复位变量
只要不被设为只读,局部变量和环境变量都可以用unset命令复位。
范例13-56
unset name; unset TERM
说明
unset命令从shell存储器中删除变量。
13.10.7 显示变量值
echo命令 内置echo命令将它的参数显示到标准输出上。echo加-e选项,允许使用大量控制输出外观的转义序列。表13-16列出了echo选项和转义序列。
表13-16 echo选项和转义序列
选 项
含 义
-e
允许解释下面列出的转义序列
-E
禁止解释这些转义字符,即使在那些默认解释它们的系统上(bash 2.x) 17
-n
删除输出结果中行尾的换行符
转义序列
/a
报警(铃)
/b
退格
/c
不带换行符打印一行
/f
换页
/n
换行
/r
回车
/t
制表符
/v
纵向制表符
//
反斜杠
/nnn
ASCII码是nnn(八进制)的字符
当使用转义序列时,不要忘记用-e开头。
范例13-57
1 $ echo The username is $LOGNAME.
The username is ellie.
2 $ echo -e \"/t/tHello there/c\"
Hello there$
3 $ echo -n \"Hello there\"
Hello there$
说明
1. echo命令将它的参数打印到屏幕上。shell会在执行echo命令之前先进行变量替换。
2. 带-e选项的echo命令支持转义序列,和C编程语言类似。$是shell提示符。
3. 当-n选项打开时,不带换行符打印一行。这个版本的echo不支持转义序列。
printf命令 printf 18的GNU版本可以用来编排打印输出的格式。它以和C printf函数相同的方式打印格式串。格式由一个串组成,它包含描述打印输出结果的格式指令。格式指令由带格式符(diouxXfeEgGcs)的%指定,%f代表一个浮点数,%d则代表一个(十进制)整数。
要得到printf格式符的完整清单以及如何使用它们,可以在命令行键入:printf --help。键入printf --version就可以知道使用的printf是什么版本。如果使用的是bash 2.x,内置printf命令所用的格式和/usr/bin下的printf可执行程序完全一样。
格式
printf格式[参数…]
范例13-58
printf \"%10.2f%5d/n\" 10.5 25
表13-17 printf命令的格式符
格 式 符
值
/\"
双引号
/0NNN
一个八进制字符,这里NNN代表0~3位
//
反斜杠
/a
报警或蜂鸣
/b
退格
/c
不产生更多的输出
/f
换页
/n
换行
/r
回车
(续表)
格 式 说 明
值
/t
水平制表符
/v
垂直制表符
/xNNN
十六进制字符,这里NNN是1~3位
%%
单个百分号
%b
字符串参数,也对/转义字符进行解释
范例13-59
1 $ printf --version
printf (GNU sh-utils) 1.16
2 $ type printf
printf is a shell builtin
3 $ printf \"The number is %.2f/n\" 100
The number is 100.00
4 $ printf \"%-20s%-15s%10.2f/n\" \"Jody\" \"Savage\" 28
Jody Savage 28.00
5 $ printf \"|%-20s|%-15s|%10.2f|/n\" \"Jody\" \"Savage\" 28
Jody |Savage | 28.00|
6 $ printf \"%s\'s average was %.1f%%./n\" \"Jody\" $(( (80+70+90)/3 ))
Jody\'s average was 80.0%.
说明
1. 显示printf命令的GNU版本。
2. 如果使用的是bash 2.x,printf是一个内置命令。
3. 按照说明符%.2f指定的格式,参数100以保留两位小数的浮点数形式输出。与C函数不同的是,这里不需要用逗号来分隔参数。
4. 格式串指明将进行3个变换:第一个是%-20s(一个左对齐,长度为20的字符串),接着是%-15s(一个左对齐,长度为15的字符串),最后一个则是%10.2f(一个右对齐,长度为10的浮点数,其中的一个字符是句点,最后两个字符是小数点右边的两个数)。参数按对应%号的顺序被格式化,因此字符串Jody对应第一个%,字符串Savage对应第二个%,数字28则对应最后一个%号。
5. 该行和第4行一样,唯一的区别是增加了竖杠以说明串是左对齐还是右对齐的。
6. printf命令格式化字符串Jody和算术扩展的结果(请参见13.13节,“算术扩展”)。需要两个百分号(%%)才能输出一个百分号(%)。
13.10.8 变量扩展修饰符
我们可以用一些专用修饰符来测试和修改变量。修饰符首先提供一个简单的条件测试,用来检查某个变量是否已经被设置,然后根据测试结果给变量赋一个值。请参见表13-18列出的变量修饰符。
表13-18 变量修饰符
修 饰 符
值
${variable:-word}
如果变量variable已被设置且非空,则代入它的值。否则,代入word
${variable:=word}
已被设置且值非空,就代入它的值。否则,将variable的值设为word。始终代入variable的值。位置参量不能用这种方式赋值
${variable:+word}
如果变量variable已被设置且值非空,代入word。否则,什么都不代入(代入空值)
${variable:?word}
如果变量variable已被设置且值非空,就代入它的值。否则,输出word并且从shell退出。如果省略了word,就会显示信息:parameter null or not set
${variable:offset}
获得变量variable值中位置从offset开始的子串,偏移为从0到串的末尾 19
${variable:offset:length}
获得变量variable值中位置从offset开始长度为length的子串
和冒号配合使用时,修饰符(-、=、+、?)检查变量是否尚未赋值或值为空。不加冒号时,值为空的变量也被认为已设置。
范例13-60
(临时替换默认值)
1 $ fruit=peach
2 $ echo ${fruit:–plum}
peach
3 $ echo ${newfruit:–apple}
apple
4 $ echo $newfruit
5 $ echo $EDITOR # More realistic example
6 $ echo ${EDITOR:-/bin/vi}
/bin/vi
7 $ echo $EDITOR
8 $ name=
$ echo ${name-Joe}
9 $ echo ${name:-Joe}
Joe
说明
1. 将变量fruit的值设为peach。
2. 这个专用修饰符将检查变量fruit是否已被设置。如果fruit已被设置,就显示它。否则,用plum替换fruit,并显示该值。
3. 变量newfruit未曾被设置。值apple将暂时替换newfruit。
4. 上一行的设置是暂时的,因此,变量newfruit仍未被设置。
5. 环境变量EDITOR尚未被设置。
6. 修饰符:-将/bin/vi替换为EDITOR。
7. EDITOR未曾被设置过,因此什么都不会打印。
8. 变量name被设为空值。因为修饰符前面没有冒号,变量即使为空也被认为是设置过的,所以没有把新的值Joe赋给变量name。
9. 冒号使得修饰符检查变量是否未设置或为空。只要是这两种情况之一,就用值Joe替换name。
范例13-61
(永久替换默认值)
1 $ name=
2 $ echo ${name:=Peter}
Peter
3 $ echo $name
Peter
4 $ echo ${EDITOR:=/bin/vi}
/bin/vi
5 $ echo $EDITOR
/bin/vi
说明
1. 赋给变量name一个空值。
2. 用修饰符:=将检查变量name是否尚未被设置。如果已经被设置过了,就不会被改变。如果尚未设置或值为空,就将等号右边的值赋给它。由于之前已将变量name设置为空,所以现在要把Peter赋给它。这个设置是持久的。
3. 变量name的值还是Peter。
4. 把变量EDITOR设置为/bin/vi。
5. 显示变量EDITOR的值。
范例13-62
(临时替换值)
1 $ foo=grapes
2 $ echo ${foo:+pears}
pears
3 $ echo $foo
grapes
$
说明
1. 将变量foo的值设置为grapes。
2. 专用修饰符:=将检查变量name是否已被设置。如果已经被设置过,就用pears暂时替换foo。否则,返回空。
3. 变量foo的值还是原来的值。
范例13-63
(基于默认值创建错误信息)
1 $ echo ${namex:?\"namex is undefined\"}
namex: namex is undefined
2 $ echo ${y?}
y: parameter null or not set
说明
1. 修饰赋:?检查变量是否已被设置。如果尚未设置该变量,就把问号右边的信息打印在标准错误输出上。如果此时是在执行脚本,就退出脚本。
2. 如果问号后面没有提供报错信息,shell就向标准错误输出发送默认的消息。
范例13-64
(创建子串 20)
1 $ var=notebook
2 $ echo ${var:0:4}
note
3 $ echo ${var:4:4}
book
4 $ echo ${var:0:2}
no
说明
1. 给变量赋值notebook。
2. var的子串从偏移0(notebook中的n)开始,长度为4个字符,在e处结束。
3. var的子串从偏移4(notebook中的b)开始,长度为4个字符,在k处结束。
4. var的子串从偏移0(notebook中的n)开始,长度为2个字符,在o处结束。
13.10.9 子串的变量扩展
模式匹配变量用来在串首或串尾截掉串的某一特定部分。这些操作符最常见的用法是从路径头或尾删除路径名元素。如表13-19所示。
表13-19 变量扩展子串
表 达 式
功 能
${变量%模式}
将变量值的尾部与模式进行最小匹配,并将匹配到的部分删除
${变量%%模式}
将变量值的尾部与模式进行最大匹配,并将匹配到的部分删除
${变量#模式}
将变量值的头部与模式进行最小匹配,并将匹配到的部分删除
${变量##模式}
将变量值的头部与模式进行最大匹配,并将匹配到的部分删除
${#变量}
替换为变量中的字符个数。如果是*或@,长度则是位置参量的个数
范例13-65
1 $ pathname=\"/usr/bin/local/bin\"
2 $ echo ${pathname%/bin*}
/usr/bin/local
说明
1. 给局部变量pathname赋值/usr/bin/local/bin。
2. %删除路径名尾部包含模式/bin,后跟零个或多个字符的最小部分。即删除/bin。
范例13-66
1 $ pathname=\"usr/bin/local/bin\"
2 $ echo ${pathname%%/bin*}
/usr
说明
1. 给局部变量pathname赋值/usr/bin/local/bin。
2. %%删除路径名尾部包含模式/bin,后跟零个或多个字符的最大部分。即删除/bin/local/bin。
范例13-67
1 $ pathname=/home/lilliput/jake/.bashrc
2 $ echo ${pathname#/home}
/lilliput/jake/.bashrc
说明
1. 给局部变量pathname赋值/home/liliput/jake/.bashrc。
2. #删除路径名头部包含模式/home的最小部分。路径变量开头的/home被删除。
范例13-68
1 $ pathname=/home/lilliput/jake/.bashrc
2 $ echo ${pathname##*/}
.bashrc
说明
1. 给局部变量pathname赋值/home/liliput/jake/.bashrc。
2. ##删除路径名的头部包含零个或多个字符,直到并包括最后一个斜杠的最大部分。即从路径变量中删除/home/liliput/jake/。
范例13-69
1 $ name=\"Ebenezer Scrooge\"
2 $ echo ${#name}
16
说明
1. 给变量name赋值Ebenezer Scrooge。
2. ${#variable}语法显示赋给变量name的字符串中字符的个数。字符串Ebenezer Scrooge中有16个字符。
13.10.10 位置参量
这组专用内置变量常常被称为位置参量,通常被shell脚本用来从命令行接收参数,或者被函数用来保存传给它的参数。这组变量之所以被称为位置参量,是因为引用它们要用到1、2、3等数字,这些数字分别代表它们在参数列表中的相应位置。请参见表13-20。
表13-20 位置参量
表 达 式
功 能
$0
指代当前shell脚本的名称
$1-$9
代表第1个到第9个位置参量
${10}
第10个位置参量
$#
其值为位置参量的个数
$*
其值为所有的位置参量
$@
除了被双引号引用的情况,含义与$*相同
\"$*\"
其值为\"$1 $2 $3\"
\"$@\"
其值为\"$1\" \"$2\" \"$3\"
shell脚本名存在变量$0中。位置参量可以用set命令来设置,重置和复位。
范例13-70
1 $ set punky tommy bert jody
$ echo $* # Prints all the positional parameters
punky tommy bert jody
2 $ echo $1 # Prints the first position
punky
3 $ echo $2 $3 # Prints the second and third position
tommy bert
4 $ echo $# # Prints the total number of positional parameters
4
5 $ set a b c d e f g h i j k l m
$ print $10 # Prints the first positional parameter followed by a 0
a0
$ echo ${10} ${11} # Prints the 10th and 11th positions
j k
6 $ echo $#
13
7 $ echo $*
a b c d e f g h i j k l m
8 $ set file1 file2 file3
$ echo /$$#
$3
9 $ eval echo /$$#
file3
10 $ set -- # Unsets all positional parameters
说明
1. set命令给位置参量赋值。专用变量$*包含所有的位置参量。
2. 显示第1个位置参量的值,punky。
3. 显示第2和第3个位置参量的值,tommy和bert。
4. 专用变量$#的值是当前已设置的位置参量的个数。
5. set命令复位所有的位置参量。原来的位置参量集被清除。要打印9以上的任意位置参量,就要用花括号把两个数字括起来。否则,就打印第一个位置参量的值,后跟另一个数。
6. 位置参量个数现在是13。
7. 显示所有位置参量的值。
8. 美元符被转义,$#是参数个数。echo命令显示$3,一个美元符号后跟位置参量的个数。
9. 执行命令之前,eval命令对命令行进行第二次解析。第一次由shell解析,将输出$3。第二次由eval解析,显示$3的值,即file3。
10. 带--选项的set命令清除或复位所有的位置参量。
13.10.11 其他特殊变量
shell有一些由单个字符组成的特殊变量。在字符前面加上美元符就能访问变量中保存的值。如表13-21所示。
表13-21 特殊变量
变 量
含 义
$
当前shell的PID
-
当前的sh选项设置
?
已执行的上一条命令的退出值
!
最后一个进入后台的作业的PID
范例13-71
1 $ echo The pid of this shell is $$
The pid of this shell is 4725
2 $ echo The options for this shell are $–
The options for this shell are imh
3 $ grep dodo /etc/passwd
$ echo $?
1
4 $ sleep 25&
4736
$ echo $!
4736
说明
1. 变量$保存这个进程的PID值。
2. 变量–列出当前这个交互式bash shell的所有选项。
3. grep命令在/etc/passwd文件中查找字符串dodo。变量?保存了上一条被执行的命令的退出状态。由于grep返回的值是1,因此可以假定grep的查找失败了。退出状态0代表成功退出。
4. 变量!保存上一条被放入后台的命令的PID号。sleep命令后面的&把命令发到后台。
13.10.1 变量类型
变量可分为两类:局部变量和环境变量。局部变量只在创建它们的shell中可用。而环境变量则可以在创建它们的shell及其派生出来的任意子进程中使用。有些变量是用户创建的,其他的则是专用shell变量。
13.10.2 命名惯例
变量名必须以字母或下划线字符开头。其余的字符可以是字母、数字(0~9)或下划线字符。任何其他的字符都标志着变量名的终止。名字是大小写敏感的。给变量赋值时,等号周围不能有任何空白符。为了给变量赋空值,可以在等号后跟一个换行符。创建一个局部变量最简单的格式是给一个变量赋值,如以下格式所示。
格式
变量=值
范例13-49
name=Tommy
13.10.3 内置命令declare
有两个内置命令可以用来创建变量,它们是declare和typeset,其选项可以控制变量设置的方式。typeset命令(来自Korn shell)的功能和declare命令(bash)完全一样。bash文档中指出,“提供typeset命令是为了和Korn shell兼容。但是不建议使用它,而应使用内置命令declare” 12。因此从这一点来说,我们将使用declare命令(即使我们使用typeset也一样方便)。
不带任何参数时,declare将列出所有已设置的变量。通常只读变量不能被重新赋值或复位。如果只读变量是用declare创建的,那它们不可以被复位,但可以被重新赋值。整型变量也可以用declare赋值。
格式
declare 变量=值
范例13-50
declare name=Tommy
表13-13 declare选项
选 项
含 义
-a 13
将变量当作一个数组。即,分配元素
-f
列出函数的名称和定义
-F
只列出函数名
-i
将变量设为整型
-r
将变量设为只读
-x
将变量名输出到子shell中
13.10.4 局部变量和作用域
变量的作用域指变量在一个程序中的哪些地方可见。对于shell来说,局部变量的作用域被限定在创建它们的shell中。
给变量赋值时,等号前后不能有空白符。如果要给变量赋空值,可以在等号后跟一个换行符 。14
变量前的美元符用来提取存储在变量里的值。
local函数可以用来创建局部变量,但仅限于在函数内使用(请参见13.16.1节,“定义函数”)。
设置局部变量 局部变量可以通过简单地赋予它一个值或一个变量名来设置,或者如范例13-51所示用declare内置函数来设置。
范例13-51
1 $ round=world or declare round=world
$ echo $round
world
2 $ name=\"Peter Piper\"
$ echo $name
Peter Piper
3 $ x=
$ echo $x
4 $ file.bak=\"$HOME/junk\"
bash: file.bak=/home/jody/ellie/junk: not found
说明
1. 将局部变量round赋值为world。如果遇到变量名前面是个美元符的情况,shell就执行变量替换。该命令显示变量round的值(不要混淆提示符($)和用来执行变量替换的$符)。
2. 局部变量name被赋值为“Peter Piper”。必须用引号来保护空白符,这样,shell分析命令行时才不会将这个字符串看成是两个词。该命令显示变量name的值。
3. 这条命令没有给变量x赋值,因此x被赋值为空。结果显示一个空值,即空字符串。
4. 变量名中出现句点是非法的。变量名可以使用的字符只能是数字、字母和下划线。因此,shell尝试将这个字符串作为一条命令来执行。
范例13-52
1 $ echo $$
1313
2 $ round=world
$ echo $round
world
3 $ bash # Start a subshell
4 $ echo $$
1326
5 $ echo $round
6 $ exit # Exits this shell, returns to parent shell
7 $ echo $$
1313
8 $ echo $round
world
说明
1. 双美元符变量的值是当前shell的PID。本例中这个shell的PID是1313。
2. 将局部变量round赋值为字符串world,并打印该变量的值。
3. 另外启动一个bash shell,这个shell被称为子shell(subshell或child shell)。
4. 当前这个shell的PID时1326,其父shell的PID则是1313。
5. 局部变量round在这个shell中没有定义,因此打印了一个空行。
6. exit命令终止当前shell并返回父shell(也可以按Ctrl+D组合键退出当前shell)。
7. 返回到父shell。显示它的PID。
8. 显示变量round的值。它是这个shell的局部变量。
设置只读变量 只读变量是不能被重新定义或复位的特殊变量。但是,如果使用了declare函数,只读变量可以被重新定义,但不能被复位。
范例13-53
1 $ name=Tom
2 $ readonly name
$ echo $name
Tom
3 $ unset name
bash: unset: name: cannot unset: readonly variable
4 $ name=Joe
bash: name: readonly variable
5 $ declare -r city=\'Santa Clara\'
6 $ unset city
bash: unset: city: cannot unset: readonly variable
7 $ declare city=\'San Francisco\' # What happened here?
$ echo $city
San Francisco
说明
1. 局部变量name的值被设为Tom。
2. 将变量name设为只读。
3. 不能复位只读变量。
4. 不能重新定义只读变量。
5. declare内置命令给只读变量city赋值Santa Clara。当所赋值串中包含空白符时必须用引号。
6. 因其是只读变量,所以不能被复位。
7. 当只读变量是由declare命令创建时,它不可以被复位,但可以被重新赋值。
13.10.5 环境变量
环境变量可用在创建它们的shell和从该shell派生的任意子shell或进程中。它们通常被称为全局变量,以区别于局部变量。通常,环境变量应该大写。环境变量是已经用export内置命令导出的变量。
变量被创建时所处的shell被称为父shell。如果父shell又启动了一个shell,这个新的shell被称作子shell。环境变量将传递给从创建它们的shell里启动的任意子进程。它们从父亲传递给儿子再到孙子等,但是不可向其他方向传递。比如,一个子进程可以创建环境变量,但不能将它传回给它的父进程,只能传给它的子进程 15。有一些环境变量,比如HOME、LOGNAME、PATH和SHELL,在用户登录之前就已经被/bin/login程序设置好了。通常,环境变量定义并保存在用户主目录下的.bash_profile文件中。请参见表13-14中列出的环境变量。
表13-14 bash环境变量
变 量 名
含 义
_(下划线)
上一条命令的最后一个参数
BASH
展开为调用bash实例时使用的全路径名
BASH_ENV
和ENV一样,但只可在bash 2.0或更高版本中设置 16
BASH_VERSINFO
使用2.0以上版本的bash时,展开为版本信息 16
BASH_VERSION
展开为当前bash实例的版本号
CDPATH
cd命令的搜索路径。它是以冒号分隔的目录列表,shell通过它来搜索cd命令指定的目标目录。例如.:~:/usr
COLUMNS
设置该变量就给shell编辑模式和选择的命令定义了编辑窗口的宽度
DIRSTACK
在2.0或以上版本的bash中,代表目录栈的当前内容
EDITOR
内置编辑器emacs、gmacs或vi的路径名
ENV
每一个新的bash shell(包括脚本)启动时执行的环境文件。通常赋予这个变量的文件名是.bashrc。ENV的值被解释为路径名前,shell先要对其进行参量扩展,命令替换和算术扩展
EUID
展开为在shell启动时被初始化的当前用户的有效ID
FCEDIT
fc命令的默认编辑器名
FIGNORE
执行文件名补全时可忽略的以冒号分隔的后缀列表。以FIGNORE中任一项为后缀的文件名被从匹配的文件名列表中排除。例如值为.o:~
FORMAT
用来格式化在命令管道上的time关键字的输出
GLOBIGNORE
在文件名扩展(称为globbing)时被忽略的文件列表
GROUPS
当前用户所属的组
HISTCMD
当前命令的历史编号或在历史清单中的序号。如果HISTCMD被复位,即使它随后就会重置,也将失去它的特殊属性
HISTCONTROL
如果设置了ignorespace值,以一个空格符开头的行将不会进入历史清单。如果设置了ignoredups值,那和前一个历史行匹配的行不会进入。值ignoreboth结合了这两个选项。如果被复位,或设置成除了上面所说的任意其他值时,所有被解释器所读的行都将保存到历史清单中
HISTFILE
指定保存命令行历史的文件。默认值是~/.bash_history。如果被复位,交互式shell退出时将不保存命令行历史
(续表)
变 量 名
含 义
HISTFILESIZE
历史文件能包含的最大行数。当给这个变量赋值后,如果有必要,历史文件将被截尾,以使包含的行数不超过这个数。默认值是500
HISTSIZE
记录在命令行历史文件中的命令数。默认是500
HOME
主目录。未指定目录时,cd命令将转向该目录
HOSTFILE
包含一个格式和/etc/hosts一样的文件的名称,当shell需要补全一个主机名时将读取该文件。文件可以交互式更改。下一次试图补全主机名时,bash将新文件的内容添加到已经存在的数据库中
HOSTTYPE
自动设置正在运行bash的机器的类型。默认值是由系统决定的
IFS
内部字段分隔符,一般是空格符、制表符和换行符,用于由命令替换,循环结构中的表和读取的输入产生的词的字段划分
IGNOREEOF
控制shell接收到单独一个EOF字符作为输入时的行为。如果设置,它的值就是shell退出前在一个输入行的最前面键入的连续EOF字符的个数。如果变量存在但没有一个数字值,或没有值,那么默认值是10。如果它不存在,EOF意味着给shell的输入的终止。它只在交互式shell中有效
INPUTRC
readline启动文件的文件名,取代默认的~/.inputrc
LANG
用来为没有以LC_开头的变量明确选取的种类确定locale类
LC_ALL
忽略LANG和任何其他LC_变量的值
LC_COLLATE
确定对路径名扩展的结果进行排序时的整理顺序,以及匹配文件名与模式时的范围表达式,等价类和整理序列的行为
LC_MESSAGES
确定用于转换前面有一个$的双引号串的locale
LINENO
每次shell在一个脚本或函数中替换代表当前连续行号(从1开始)的十进制数时,都将引用该参数
MACHTYPE
包含一个描述正在运行bash的系统的串
如果该参数被设置为某个邮件文件的名称,而MAILPATH未被设置,当邮件到达MAIL指定的文件时,shell会通知用户
MAIL_WARNING
如果设置了该变量,当bash发现用于检查邮件的文件在上次检查后又被访问了,将打印消息“The mail in [filename where mail is stored] has been read”
MAILCHECK
这个参数定义shell将隔多长时间(以秒为单位)检查一次由参数MAILPATH或MAILFILE指定的文件,看看是否有邮件到达。默认值是600秒(10分钟)。如果将它设为0,shell每次输出主提示符之前都会去检查邮件
MAILPATH
由冒号分隔的文件名列表。如果设置了这个参数,只要有邮件到达任何一个由它指定的文件,shell都会通知用户。每个文件名后面都可以跟一个百分号和一条消息,当文件修改时间发生变化时,shell会显示这条消息。默认的消息是:You have mail
(续表)
变 量 名
含 义
OLDPWD
前一个工作目录
OPTARG
上一个由getopts内置命令处理的选项参数的值
OPTERR
如果设置成1,显示来自getopts内置命令的错误信息
OPTIND
下一个由getopts内置命令处理的参数的序号
OSTYPE
自动设置成一个串,该串描述正在运行bash的操作系统。默认值由系统决定
PATH
命令搜索路径。一个由冒号分隔的目录列表,shell用它来搜索命令。默认路径由系统决定,并且由安装bash的管理员设置。一个普通值为
/usr/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin:
PIPESTATUS
一个数组,包含一列最近在管道执行的前台作业的进程退出状态值
PPID
父进程的进程ID
PROMPT_COMMAND
赋给这个变量的命令将在主提示符显示前执行
PS1
主提示符串,默认值是$
PS2
次提示符串,默认值是>
PS3
与select命令一起使用的选择提示符串,默认值是#?
PS4
当开启追踪时使用的调试提示符串,默认值是+。追踪可以用set –x开启
PWD
当前工作目录。由cd设置
RANDOM
每次引用该变量,就产生一个随机整数。随机数序列可以通过给RANDOM赋值来初始化。如果RANDOM被复位,即使随后再设置,它也将失去特定的属性
REPLY
当没有给read提供参数时设置
SECONDS
每次SECONDS被引用,将返回调用shell以来的秒数。如果给SECONDS赋一个值,以后引用返回的值将是赋值以来的秒数加上所赋的值。如果SECONDS被复位,即使随后再设置,它也将失去特定的属性
SHELL
当调用shell时,它扫描环境变量以寻找该名字。shell给PATH、PS1、PS2、MAILCHECK和IFS设置默认值。HOME和MAIL由login(1)设置
SHELLOPTS
包含一列开启的shell选项,比如braceexpand、hashall、monitor等
SHLVL
每启动一个bash实例时将其加1
TMOUT
设置退出前等待输入的秒数
UID
展开为当前用户的用户ID,在shell启动时初始化
设置环境变量 如果想设置环境变量,就要在给变量赋值之后或设置变量时使用export命令(参见表13-15)。带-x选项的declare内置命令也可完成同样的功能(输出变量时不要在变量名前面加$)。
表13-15 export命令和它的选项
选 项
值
--
标志着选项末尾,余下的参数被视为变量
-f
名-值对被看作函数,而不是变量
-n
将一个全局(导出)变量转换成局部变量。之后该变量将不能被导出到子进程中
-p
显示所有的全局变量
格式
export变量=值
变量=值; export变量
declare –x 变量=值
范例13-54
export NAME=john
PS1= \'/d:/W:$USER> \' ; export PS1
declare -x TERM=sun
范例13-55
1 $ export TERM=sun # or declare -x TERM=sun
2 $ NAME=\"John Smith\"
$ export NAME
$ echo $NAME
John Smith
3 $ echo $$
319 # pid number for parent shell
4 $ bash # Start a subshell
5 $ echo $$
340 # pid number for new shell
6 $ echo $NAME
John Smith
7 $ declare -x NAME=\"April Jenner\"
$ echo $NAME
April Jenner
8 $ exit # Exit the subshell and go back to parent shell
9 $ echo $$
319 # pid number for parent shell
10 $ echo $NAME
John Smith
说明
1. 给TERM变量赋值sun。同时输出它。现在,由这个shell启动的所有进程都将继承这个变量。也可以用declare –x来完成同样的功能。
2. 定义并输出变量NAME,让它可以被当前shell启动的所有子shell使用。
3. 打印当前shell的PID的值。
4. 启动一个新的bash。这个新shell称为子shell。原来那个shell称为父shell。
5. 新的bash shell的PID保存在变量$$中,回显这个变量的值。
6. 在父shell中设置的变量NAME导出给这个新shell,这条命令显示它的值。
7. 内置的declare函数是设置变量的另一种方式。用-x开关,declare可以输出变量。该变量被重新设置为April Jenner。这个变化将输出到所有的子shell,但不会影响父shell。输出的变量不会向上传递给父shell。
8. 退出这个bash子shell。
9. 再次显示父shell的PID。
10. 变量NANE的值还跟原来一样。从父shell输出到子shell时,变量保持它们的值不变。子shell不可能改变父shell的变量的值。
13.10.6 复位变量
只要不被设为只读,局部变量和环境变量都可以用unset命令复位。
范例13-56
unset name; unset TERM
说明
unset命令从shell存储器中删除变量。
13.10.7 显示变量值
echo命令 内置echo命令将它的参数显示到标准输出上。echo加-e选项,允许使用大量控制输出外观的转义序列。表13-16列出了echo选项和转义序列。
表13-16 echo选项和转义序列
选 项
含 义
-e
允许解释下面列出的转义序列
-E
禁止解释这些转义字符,即使在那些默认解释它们的系统上(bash 2.x) 17
-n
删除输出结果中行尾的换行符
转义序列
/a
报警(铃)
/b
退格
/c
不带换行符打印一行
/f
换页
/n
换行
/r
回车
/t
制表符
/v
纵向制表符
//
反斜杠
/nnn
ASCII码是nnn(八进制)的字符
当使用转义序列时,不要忘记用-e开头。
范例13-57
1 $ echo The username is $LOGNAME.
The username is ellie.
2 $ echo -e \"/t/tHello there/c\"
Hello there$
3 $ echo -n \"Hello there\"
Hello there$
说明
1. echo命令将它的参数打印到屏幕上。shell会在执行echo命令之前先进行变量替换。
2. 带-e选项的echo命令支持转义序列,和C编程语言类似。$是shell提示符。
3. 当-n选项打开时,不带换行符打印一行。这个版本的echo不支持转义序列。
printf命令 printf 18的GNU版本可以用来编排打印输出的格式。它以和C printf函数相同的方式打印格式串。格式由一个串组成,它包含描述打印输出结果的格式指令。格式指令由带格式符(diouxXfeEgGcs)的%指定,%f代表一个浮点数,%d则代表一个(十进制)整数。
要得到printf格式符的完整清单以及如何使用它们,可以在命令行键入:printf --help。键入printf --version就可以知道使用的printf是什么版本。如果使用的是bash 2.x,内置printf命令所用的格式和/usr/bin下的printf可执行程序完全一样。
格式
printf格式[参数…]
范例13-58
printf \"%10.2f%5d/n\" 10.5 25
表13-17 printf命令的格式符
格 式 符
值
/\"
双引号
/0NNN
一个八进制字符,这里NNN代表0~3位
//
反斜杠
/a
报警或蜂鸣
/b
退格
/c
不产生更多的输出
/f
换页
/n
换行
/r
回车
(续表)
格 式 说 明
值
/t
水平制表符
/v
垂直制表符
/xNNN
十六进制字符,这里NNN是1~3位
%%
单个百分号
%b
字符串参数,也对/转义字符进行解释
范例13-59
1 $ printf --version
printf (GNU sh-utils) 1.16
2 $ type printf
printf is a shell builtin
3 $ printf \"The number is %.2f/n\" 100
The number is 100.00
4 $ printf \"%-20s%-15s%10.2f/n\" \"Jody\" \"Savage\" 28
Jody Savage 28.00
5 $ printf \"|%-20s|%-15s|%10.2f|/n\" \"Jody\" \"Savage\" 28
Jody |Savage | 28.00|
6 $ printf \"%s\'s average was %.1f%%./n\" \"Jody\" $(( (80+70+90)/3 ))
Jody\'s average was 80.0%.
说明
1. 显示printf命令的GNU版本。
2. 如果使用的是bash 2.x,printf是一个内置命令。
3. 按照说明符%.2f指定的格式,参数100以保留两位小数的浮点数形式输出。与C函数不同的是,这里不需要用逗号来分隔参数。
4. 格式串指明将进行3个变换:第一个是%-20s(一个左对齐,长度为20的字符串),接着是%-15s(一个左对齐,长度为15的字符串),最后一个则是%10.2f(一个右对齐,长度为10的浮点数,其中的一个字符是句点,最后两个字符是小数点右边的两个数)。参数按对应%号的顺序被格式化,因此字符串Jody对应第一个%,字符串Savage对应第二个%,数字28则对应最后一个%号。
5. 该行和第4行一样,唯一的区别是增加了竖杠以说明串是左对齐还是右对齐的。
6. printf命令格式化字符串Jody和算术扩展的结果(请参见13.13节,“算术扩展”)。需要两个百分号(%%)才能输出一个百分号(%)。
13.10.8 变量扩展修饰符
我们可以用一些专用修饰符来测试和修改变量。修饰符首先提供一个简单的条件测试,用来检查某个变量是否已经被设置,然后根据测试结果给变量赋一个值。请参见表13-18列出的变量修饰符。
表13-18 变量修饰符
修 饰 符
值
${variable:-word}
如果变量variable已被设置且非空,则代入它的值。否则,代入word
${variable:=word}
已被设置且值非空,就代入它的值。否则,将variable的值设为word。始终代入variable的值。位置参量不能用这种方式赋值
${variable:+word}
如果变量variable已被设置且值非空,代入word。否则,什么都不代入(代入空值)
${variable:?word}
如果变量variable已被设置且值非空,就代入它的值。否则,输出word并且从shell退出。如果省略了word,就会显示信息:parameter null or not set
${variable:offset}
获得变量variable值中位置从offset开始的子串,偏移为从0到串的末尾 19
${variable:offset:length}
获得变量variable值中位置从offset开始长度为length的子串
和冒号配合使用时,修饰符(-、=、+、?)检查变量是否尚未赋值或值为空。不加冒号时,值为空的变量也被认为已设置。
范例13-60
(临时替换默认值)
1 $ fruit=peach
2 $ echo ${fruit:–plum}
peach
3 $ echo ${newfruit:–apple}
apple
4 $ echo $newfruit
5 $ echo $EDITOR # More realistic example
6 $ echo ${EDITOR:-/bin/vi}
/bin/vi
7 $ echo $EDITOR
8 $ name=
$ echo ${name-Joe}
9 $ echo ${name:-Joe}
Joe
说明
1. 将变量fruit的值设为peach。
2. 这个专用修饰符将检查变量fruit是否已被设置。如果fruit已被设置,就显示它。否则,用plum替换fruit,并显示该值。
3. 变量newfruit未曾被设置。值apple将暂时替换newfruit。
4. 上一行的设置是暂时的,因此,变量newfruit仍未被设置。
5. 环境变量EDITOR尚未被设置。
6. 修饰符:-将/bin/vi替换为EDITOR。
7. EDITOR未曾被设置过,因此什么都不会打印。
8. 变量name被设为空值。因为修饰符前面没有冒号,变量即使为空也被认为是设置过的,所以没有把新的值Joe赋给变量name。
9. 冒号使得修饰符检查变量是否未设置或为空。只要是这两种情况之一,就用值Joe替换name。
范例13-61
(永久替换默认值)
1 $ name=
2 $ echo ${name:=Peter}
Peter
3 $ echo $name
Peter
4 $ echo ${EDITOR:=/bin/vi}
/bin/vi
5 $ echo $EDITOR
/bin/vi
说明
1. 赋给变量name一个空值。
2. 用修饰符:=将检查变量name是否尚未被设置。如果已经被设置过了,就不会被改变。如果尚未设置或值为空,就将等号右边的值赋给它。由于之前已将变量name设置为空,所以现在要把Peter赋给它。这个设置是持久的。
3. 变量name的值还是Peter。
4. 把变量EDITOR设置为/bin/vi。
5. 显示变量EDITOR的值。
范例13-62
(临时替换值)
1 $ foo=grapes
2 $ echo ${foo:+pears}
pears
3 $ echo $foo
grapes
$
说明
1. 将变量foo的值设置为grapes。
2. 专用修饰符:=将检查变量name是否已被设置。如果已经被设置过,就用pears暂时替换foo。否则,返回空。
3. 变量foo的值还是原来的值。
范例13-63
(基于默认值创建错误信息)
1 $ echo ${namex:?\"namex is undefined\"}
namex: namex is undefined
2 $ echo ${y?}
y: parameter null or not set
说明
1. 修饰赋:?检查变量是否已被设置。如果尚未设置该变量,就把问号右边的信息打印在标准错误输出上。如果此时是在执行脚本,就退出脚本。
2. 如果问号后面没有提供报错信息,shell就向标准错误输出发送默认的消息。
范例13-64
(创建子串 20)
1 $ var=notebook
2 $ echo ${var:0:4}
note
3 $ echo ${var:4:4}
book
4 $ echo ${var:0:2}
no
说明
1. 给变量赋值notebook。
2. var的子串从偏移0(notebook中的n)开始,长度为4个字符,在e处结束。
3. var的子串从偏移4(notebook中的b)开始,长度为4个字符,在k处结束。
4. var的子串从偏移0(notebook中的n)开始,长度为2个字符,在o处结束。
13.10.9 子串的变量扩展
模式匹配变量用来在串首或串尾截掉串的某一特定部分。这些操作符最常见的用法是从路径头或尾删除路径名元素。如表13-19所示。
表13-19 变量扩展子串
表 达 式
功 能
${变量%模式}
将变量值的尾部与模式进行最小匹配,并将匹配到的部分删除
${变量%%模式}
将变量值的尾部与模式进行最大匹配,并将匹配到的部分删除
${变量#模式}
将变量值的头部与模式进行最小匹配,并将匹配到的部分删除
${变量##模式}
将变量值的头部与模式进行最大匹配,并将匹配到的部分删除
${#变量}
替换为变量中的字符个数。如果是*或@,长度则是位置参量的个数
范例13-65
1 $ pathname=\"/usr/bin/local/bin\"
2 $ echo ${pathname%/bin*}
/usr/bin/local
说明
1. 给局部变量pathname赋值/usr/bin/local/bin。
2. %删除路径名尾部包含模式/bin,后跟零个或多个字符的最小部分。即删除/bin。
范例13-66
1 $ pathname=\"usr/bin/local/bin\"
2 $ echo ${pathname%%/bin*}
/usr
说明
1. 给局部变量pathname赋值/usr/bin/local/bin。
2. %%删除路径名尾部包含模式/bin,后跟零个或多个字符的最大部分。即删除/bin/local/bin。
范例13-67
1 $ pathname=/home/lilliput/jake/.bashrc
2 $ echo ${pathname#/home}
/lilliput/jake/.bashrc
说明
1. 给局部变量pathname赋值/home/liliput/jake/.bashrc。
2. #删除路径名头部包含模式/home的最小部分。路径变量开头的/home被删除。
范例13-68
1 $ pathname=/home/lilliput/jake/.bashrc
2 $ echo ${pathname##*/}
.bashrc
说明
1. 给局部变量pathname赋值/home/liliput/jake/.bashrc。
2. ##删除路径名的头部包含零个或多个字符,直到并包括最后一个斜杠的最大部分。即从路径变量中删除/home/liliput/jake/。
范例13-69
1 $ name=\"Ebenezer Scrooge\"
2 $ echo ${#name}
16
说明
1. 给变量name赋值Ebenezer Scrooge。
2. ${#variable}语法显示赋给变量name的字符串中字符的个数。字符串Ebenezer Scrooge中有16个字符。
13.10.10 位置参量
这组专用内置变量常常被称为位置参量,通常被shell脚本用来从命令行接收参数,或者被函数用来保存传给它的参数。这组变量之所以被称为位置参量,是因为引用它们要用到1、2、3等数字,这些数字分别代表它们在参数列表中的相应位置。请参见表13-20。
表13-20 位置参量
表 达 式
功 能
$0
指代当前shell脚本的名称
$1-$9
代表第1个到第9个位置参量
${10}
第10个位置参量
$#
其值为位置参量的个数
$*
其值为所有的位置参量
$@
除了被双引号引用的情况,含义与$*相同
\"$*\"
其值为\"$1 $2 $3\"
\"$@\"
其值为\"$1\" \"$2\" \"$3\"
shell脚本名存在变量$0中。位置参量可以用set命令来设置,重置和复位。
范例13-70
1 $ set punky tommy bert jody
$ echo $* # Prints all the positional parameters
punky tommy bert jody
2 $ echo $1 # Prints the first position
punky
3 $ echo $2 $3 # Prints the second and third position
tommy bert
4 $ echo $# # Prints the total number of positional parameters
4
5 $ set a b c d e f g h i j k l m
$ print $10 # Prints the first positional parameter followed by a 0
a0
$ echo ${10} ${11} # Prints the 10th and 11th positions
j k
6 $ echo $#
13
7 $ echo $*
a b c d e f g h i j k l m
8 $ set file1 file2 file3
$ echo /$$#
$3
9 $ eval echo /$$#
file3
10 $ set -- # Unsets all positional parameters
说明
1. set命令给位置参量赋值。专用变量$*包含所有的位置参量。
2. 显示第1个位置参量的值,punky。
3. 显示第2和第3个位置参量的值,tommy和bert。
4. 专用变量$#的值是当前已设置的位置参量的个数。
5. set命令复位所有的位置参量。原来的位置参量集被清除。要打印9以上的任意位置参量,就要用花括号把两个数字括起来。否则,就打印第一个位置参量的值,后跟另一个数。
6. 位置参量个数现在是13。
7. 显示所有位置参量的值。
8. 美元符被转义,$#是参数个数。echo命令显示$3,一个美元符号后跟位置参量的个数。
9. 执行命令之前,eval命令对命令行进行第二次解析。第一次由shell解析,将输出$3。第二次由eval解析,显示$3的值,即file3。
10. 带--选项的set命令清除或复位所有的位置参量。
13.10.11 其他特殊变量
shell有一些由单个字符组成的特殊变量。在字符前面加上美元符就能访问变量中保存的值。如表13-21所示。
表13-21 特殊变量
变 量
含 义
$
当前shell的PID
-
当前的sh选项设置
?
已执行的上一条命令的退出值
!
最后一个进入后台的作业的PID
范例13-71
1 $ echo The pid of this shell is $$
The pid of this shell is 4725
2 $ echo The options for this shell are $–
The options for this shell are imh
3 $ grep dodo /etc/passwd
$ echo $?
1
4 $ sleep 25&
4736
$ echo $!
4736
说明
1. 变量$保存这个进程的PID值。
2. 变量–列出当前这个交互式bash shell的所有选项。
3. grep命令在/etc/passwd文件中查找字符串dodo。变量?保存了上一条被执行的命令的退出状态。由于grep返回的值是1,因此可以假定grep的查找失败了。退出状态0代表成功退出。
4. 变量!保存上一条被放入后台的命令的PID号。sleep命令后面的&把命令发到后台。