命令执行的结果可以保存到文件中或变量内。因为变量是保存在内存中的,它们可以比文件更快的进行查阅。Bash对变量的大小不进行限制:它们足以保存任何你想要保存的内容。
对于外壳脚本程序来说使用变量是最基本的要求。本章将深度讨论变量并阐释如何在外壳脚本程序中使用它们,例如从基本的变量使用到使用eval命令扩展变量。
使用Bash的declare命令可以声明一个变量,例如声明变量COST使用下面的命令:
$ declare COST
为了兼容Korn外壳语言,你也可以使用内置命令typeset语句。如果你使用Bash,declare命令已经可以具备旧命令的所有特性了。
选择一个好的变量名是非常重要的。以前,我给一个大学一年级的计算机专业课程进行评分,这个学生使用了26个变量从A到Z。而没有任何对变量的解释,花了我半个小时来搞清楚这个程序的用途是什么。
命令的规则也应该是一致的。例如在共有基金行业中,portfolios、accounts、funds、通常是一个含义。在你的脚本中最好只使用其中一个。如果三个都用,读者也许它们表示着不同的东西。
即使将nounset参数打开,因为Bash不对变量名做最小化的检查,另一个通常的错误是使用了看上去类似的变量。在我大学一年级时,我遇到了一个老朋友,他正为他的Fortran程序不能工作而沮丧。当我仔细检查了他的程序,我看到他有一个变量声明叫做HIGH,而在程序中他用的是HI。当程序显示HIGH值后,总是显示为零。因为它从来就没被赋过值。这种情况也会发生在Bash脚本中。
变量使用字母开头,下划线后面跟着字符或数字或者下划线。
虽然变量名可以是大写或着小写,传统规则上,变量名是大写,而且不会和外壳命令混淆(外壳命令一般都是小写)。TOTAL/ORDERS_EUROPE和_W3C都是合法的变量命令。外壳并不使用特定目的的保留字。
变量可以使用等于号(=)给变量赋值。下面示例显示了如何给变量分配空的字符串:
$ COST=
否则分配一些文本给变量:
$ COST=0
虽然printf %d显示零值和空的字符串,Bash认为两个值是不同的。一个变量没有值和一个变量值为0
第6章展示了如何使用let命令分配变量值。
Bash如何区分变量名和变量值呢?引用变量值,你需要在变量名之前加上美元符号(“$”)。
$ printf “%s” $COST
0
美元符号意味着替换。外壳使用COST值替换$COST。本例中COST的值是0,在替换了COST的值之后,命令变成了:
$ printf “%d” 0
Bash执行命令并显示0。
当变量在首次声明时可以分配一个初始值。
$ declare COST=5
$ printf “%d” $COST
5
因为declare是一个命令,执行declare命令时,变量才被建立。脚本结束之后它们还存在,除非你使用内置命令unset将变量销毁。
$ unset COST
unset命令是一个命令行命令,一个风格良好的脚本,总是在脚本开始时声明变量,在结束时使用unset命令。这样变量可以在整个脚本中起作用,编程者不会担心变量使用的混淆。
命令的结果也可以分配给变量。如果命令包含倒引号内,命令输出的所有内容保存在变量内。
$ declare NUMBER_OF_FILES
$ NUMBER_OF_FILES=’ls -1 | wc -l’
$ printf “%d” “$NUMBER_OF_FILES”
14
Bash有50多个预定的变量。这些变量在Bash第一次开始运行已经建立了,它们提供了关于Bash会话的信息还可以用于控制外壳的特性。
其中一些变量在你使用了unset命令设置了它们,它们会丢失原先的属性,接着他们会使用相同的名字从新建立一个。例如:RANDOM变量包含一个随机数,如果你使用unset删除了RANDOM变量,系统又声明了一个叫RANDOM的变量。新的变量是一个正常的外壳变量,但是不包含随机数。因此,最好避免建立相同名字的变量做为预定义变量。
declare(没有开关)命令列出当前所有的已定义变量。
n BASH—Bash的全路径名
n BASH_ENV—在一个外壳脚本中,在脚本开始执行前的配置文件名
n BASH_VERSION—Bash的版本号
n COLUMNS—每行要显示的列数(例如:80)
n FUNCNAME—在子功能中,这个子功能的名字
n HOSTNAME—计算机名称或域名称
n HOSTTYPE—Type of computer.
n HOME—home的目录名
n IFS—内部字段分隔符用于将一行分割为单词
n LINENO—在脚本或子程序的当前行号
n LINES—要显示的行数(例如:24)
n OSTYPE—操作系统的名字
n PATH—要执行的命令的查找路径列表(使用“:”分割)n
n PPID—外壳父进程的进程号n
n PROMPT_COMMAND—在设置PS1主提示字符串之前要执行的命令
n PS1—主提示字符串n
n PS2—第二个提示字符串n
n PS3—选择命令提示字符串
n PS4—跟踪命令输出前缀字符串
n PWD—当前的工作路径 (和使用cd命令类似)
n RANDOM—返回一个0~32767之间的一个随机数
n SHELL—要使用的外壳或程序使用的外壳
n TERM—终端模拟类型 (例如:console )
Linux发行版还定义了一些额外的变量。这些变量的出现依赖于特定的发行版。它们中的许多是为应用程序的使用而定义。
n DISPLAY ——是X Window 显示服务器
n EDITOR ——是缺省的编辑器,在可视化编辑器出现前它定义了一个行编辑器
n ORGANIZATION——机构的名字(通常是/etc/organization中的内容)
n TERM——终端模拟器(例如:xterm session使用xterm或者Linux 使用控制台)
n VISUAL——是一个编辑器和EDITOR.相同
n WINDOWMANAGER——当前X Windows窗口管理器的路径
在本章的结尾部分有一份完整的列表。
熟悉其他计算机语言的那些人可能会被Bash使用引号的方法给弄混淆了。单引号和双引号不是用来描述字符或字符串的。而是控制外壳外壳如何将字符进行分组或表示字符串中特定的字符。Bash调用word splitting进程。
$ COST=0
$ COST=”0”
这两种形式的分配有相同的结果:COST被赋值为0。双引号显式的表明COST被分配的值包含字符0。短的包含在文本中数字的值可以直接分配给变量而不用双引号。可是,当字符串包含空格,它不再是外观上显示的要分配的值,可以看一下下面的事例:
$ DISTRIBUTION_CENTERS=London
$ printf “%s” $DISTRIBUTION_CENTERS
London
$ DISTRIBUTION_CENTERS=London ; Paris ; New York
bash: Paris: command not found
bash: New: command not found
$ printf “%s” $DISTRIBUTION_CENTERS
London
当Bash检查第二个分配的值时,遇到了空格,它就把第一个参数分配给了变量。接着,它又遇到了分号,Bash认为这是一个新的语句,他尝试着执行这条Paris语句。New也是这种情况,并把Youk认为是New的参数。
基于上面的情况,最好的方法是把所有的参数都使用双引号给包含起来。即使你的工资只有一个发行中心London,你也最好把它用双引号给括起来,以便以后可以随时在增加一个发行中心,而不会引起外壳脚本的崩溃。使用双引号封闭起来的值应该是Bash已经明确知道要分配的值。
$ DISTRIBUTION_CENTERS=”London ; Paris ; New York”
$ printf “%s” $DISTRIBUTION_CENTERS
London;Paris;NewYork
上面这个结果任然不是正确的。Bash把DISTRIBUTION_CENTERS的值给拿走了,并把其中的空格给去除了,导致printf不能将它的参数进行正确显示。printf的参数必须使用双引号封闭起来,使变量的值作为一个单独的带有空格的参数,这样才能正确的显示。
$ printf “%s” “$DISTRIBUTION_CENTERS”
London ; Paris ; New York
最好引用变量时最好都使用双引号给封闭起来。
因为引号不是分隔符只是暗示着如何解释特定的字符,它们可以背靠背的使用。可以使用这种方法将一个字符串中方便的引入一个变量。
$ TAX=7.25
$ TAX_MESSAGE=”The tax is “”$TAX””%”
$ printf “%s” “$TAX_MESSAGE”
The tax is 7.25%
使用空格将引号封闭起来的那部分分开会导致先前同样的问题:Bash将它们认为是三个独立的部分。
有种替换方法是使用大括号将变量给封闭起来,这样一目了然,容易识别。
$ TAX_MESSAGE=”The tax is ${TAX}%”
除了空格的处理,引号的另一个作用是不使用模版进行匹配。例如:星号(“*”)表示当前目录中的所有文件。引号阻止星号来匹配所有的文件。
$ printf “%s/n” *
orders.txt
archive
calc.sh
$ printf “%s/n” “*”
*
为了不解释字符串中特殊字符,可以使用单引号。双引号并不能阻止Bash解释特殊字符例如:“$”和“/”,但是单引号可以保持所有的字符都显示出来。
$ printf “%s” ‘$TAX_MESSAGE’
$TAX_MESSAGE
本例中,使用的单引号没有将其解释为变量,因为“$”符号没有被特殊对待。
斜杠“/”就像单引号封闭的耽搁字符,不将字符特殊对待,保持跟着的字符原样输出。例如:显示一个双引号:
$ printf “%s” “/””
“
本例中的斜杠表示后面的双引号作为一个普通的字符对待,而不是作为一对双引号的结束对待。
printf格式代码“%q”在每一个字符之前显示一个斜杠,使用这个可以保证空格被保留下来。
$ printf “%q” “$TAX_MESSAGE”
The/ tax/ is/ 7.25%
例如:从文件中读取操作被“%q”所影响,如果显示包含空格的变量,读到的字符作为分隔符,除非他们使用了斜杠。
$ printf “%q %q/n” “Alpha Systems Inc” “Key West, Florida” > company.txt
$ read COMPANY LOCATION < company.txt
$ printf “%s/n” “$COMPANY”
Alpha Systems Inc
$ printf “%s %s/n” “Alpha Systems Inc” “Key West, Florida” > company.txt
$ read COMPANY LOCATION < company.txt
$ printf “%s/n” “$COMPANY”
Alpha
read命令一行内的那些字符属于哪一个变量,他假设变量值是用空格进行分割的,上例中,它会把Alpha分配给COMPANY。当使用“%q”后,read命令知道使用斜杠作为前缀的空格属于第一个值,它会一直读取到没有斜杠保护的空格,将Alpha System Inc分配给COMPANY。
单词分割字符由IFS变量的值所控制,通常IFS将空格,tab和换行作为单词分隔符。即:它使用白空格字符。如果IFS的内容改变,单词分隔符将使用新定义的字符。但是,为了和以前的定义兼容,最好不要更改IFS变量,通过使用“%q”格式字符可以在脚本中使用空格。
无论在分配值或使用美元符号“$”引入变量时最好否使用双引号将引用的变量封闭起来,以防变量中有空格的导致出现问题。
所有的Bash变量都被简单的存储为字符串。每一个变量都包含有一些选项,我们称之为属性。它可以类似于外壳选项和shopt命令的方式来使用declare命令打开或关闭。
如果变量使用-i开关,Bash打开了变量的整数属性。外壳将会记住这串字符应该以整数值来对待。如果是非整数值分配给了整数变量,Bash将不会报错而是将这个变量的值赋为零。
$ declare -i NUMBER_ACCOUNTS=15
$ printf “%d/n” “$NUMBER_ACCOUNTS”
15
$ NUMBER_ACCOUNTS=”Smith” # mistake
$ printf “%d/n” “$NUMBER_ACCOUNTS”
0
$ NUMBER_ACCOUNTS=12
$ printf “%d/n” “$NUMBER_ACCOUNTS”
12
有时再次分配一串字符给整形变量会产生错误,但是你最好不要依赖这种方式进行纠错。
$ NUMBER_ACCOUNTS=”Smith Account” # mistake
bash: Smith Account: syntax error in expression (error token is “Account”)
变量的属性可以使用-p开关显示出来:
$ declare -p NUMBER_ACCOUNTS
declare -i NUMBER_ACCOUNTS=”12”
信息以这种方式显示可以被保存起来供其他脚本使用。如果你认为显示的结果满足要求,你可以将这段声明写入你的脚本文件中。
整形属性可以使用加号(“+”)给关闭掉。
$ declare +i NUMBER_ACCOUNTS # turn off integer attribute
$ printf “%d/n” “$NUMBER_ACCOUNTS”
bash: printf: Smith Account: invalid number
$ printf “%s/n” “$NUMBER_ACCOUNTS”
Smith Account
虽然Bash并不认为分配给变量一个非数字值是一个错误,但是printf命令会报告说不能显示一个非数字的值。
像printf命令一样,整形变量也可以分配一个八进制或十六进制的数。
$ declare -i NUMBER_ACCOUNTS=0X0F
$ printf “%i/n” “$NUMBER_ACCOUNTS”
15
常量是不变化的变量,创建时可以使用-r开关,如果你要从新给一个常量分配一个新值,Bash会报错。假设常量COMPANY有一个公司的名字。
$ declare -r COMPANY=”Smith and Jones”
$ printf “%s/n” “$COMPANY”
Smith and Jones
$ COMPANY=”Wilson Distribution”
bash: COMPANY: readonly variable
readonly属性可以使用加号来关闭掉。可以,这将导致其他人读你的脚本时搞不清楚到底是什么意思,他们可能认为readonly将一直是不会改变的。你可以删除readonly属性或者更改脚本的结构。
数组是许多值的列表,它们可以通过使用-a(array)属性来创建。在数组中有一个索引号来代表数组的每一个项目。Bash数组的定义不同于其他计算机语言,因为他们的结束可以是无止尽的。数组可以是任意长度的并且开始时每个项目填充为空的字符串。
$ declare -a PRODUCTS
给数组分配一个新数值时,数组名后跟着的方括号和数字表示它在数组中的位置。第一个位置的数字是零(0),如果要定义初始值,应该给它的第一个位置分配值。给数组分配一个值并不是特别有用,但是这样会和其他外壳语言兼容。相应的也可以给特定位置的变量分配一个初始值该位置由方括号[]内的数字来制定。
$ declare -a DEPT[0]=”accounting” DEPT [1]=”shipping” /
DEPT [2]=”customer service”
使用大括号来制定变量名和相匹配的外壳路径名。
$ echo “${ DEPT [0]}”
accounting
$ echo “${ DEPT [2]}”
customer service
所有在数组中没有制定位置的变量没有值,例如:在PRODUCTS数组中位置5初始化为一个空字符串,它可以使用分配语句将该位置分配为“hammers”。
$ printf “%s” “${PRODUCTS[5]}”
$ PRODUCTS[5]=”hammers”
$ printf “%s” “${PRODUCTS[5]}”
hammers
如果在位置0有一个值,当不指定位置时默认返回位置0的值。
$ PRODUCTS[0]=”screwdrivers”
$ printf “%s” “$PRODUCTS”
screwdrivers
$ printf “%s” “${PRODUCTS[0]}”
Screwdrivers
如果要返回整个数组可以使用星号“*”或标记符号“@”返回数组中所有的值。两者的区别是当它们在双引号中时,星号返回一个字符串,成员之间用IFS变量分割(IFS通常是空格),标记符号把每个成员作为一个独立的字符串,它们之间没有分割字符串。
$ printf “%s” “${PRODUCTS[*]}”
screwdrivers hammers
/home/kburtch [bash]
$ printf “%s” “${PRODUCTS[@]}”
Screwdrivershammers
在下面的示例中,标记形式需要两个分开的%s格式代码来正确显示数组成员。
$ printf “%s %s/n” “${PRODUCTS[@]}”
screwdrivers hammers
可以在小括号“()”中使用一个列表来分配多个成员。
$ DIVISIONS=(“North America” “Europe” “Far East”)
$ printf “%s/n” “${DIVISIONS[*]}”
North America Europe Far East
上面的方式也可以使用下面的表达式来分配。
$ DIVISIONS=([3]=”North America” [2]=”Europe” [1]=”Far East”)
$ printf “%s/n” “${DIVISIONS[*]}”
Far East Europe North America
使用declare命令可以使数组在建立时就分配值。
使用在方括号中标记符号或星号,可以在变量名前加井号“#”来返回数组成员数。这些成员不必是连续的,返回的数也不能反映它们存储的位置。
$ printf “%d” “${#PRODUCTS[*]}”
2
删除数组中单独的值使用unset命令。通过给数组成员分配空字符串并不能销毁它,这个空的字符串在计数时仍然有效。
命令read使用-a开关可以将一个列表导入到一个数组中。使用这个开关时,一行中的每一个成员都作为数组的一个成员。
属性array只是一个变量属性,它可以在打开之后再关闭。如果Bash允许关闭这个属性,数组变成一个普通的变量时,数组中的成员将会丢失。
在脚本或交互会话中声明的变量只存在于它们声明的地方,为了使脚步变量在声明之外也有效,必须声明为可输出的。变量使用declare –x(export)开关将自己的export属性置为可输出的。属性export提醒外壳,你想通过运行这个脚步输出某个变量给所有的程序。
例如:程序cvs需要一个CVSROOT的变量给所有其他程序使用。
$ declare -x CVSROOT=”/home/cvs/cvsroot”
同样,任何在配置脚本文件中声明的变量必须输出否则它们就不存在于命令提示符下。在配置脚本文件运行结束后,这些声明就会消失。
而变量输出后,配置脚本程序对变量所作的更改在程序结束后会被丢弃。如果,第二个脚本更改了CVSROOT变量为/home/local/cvsroot,当第二个脚本程序运行结束,CVSROOT将改回/home/cvs/cvsroot。这种更改称之为会滚。
建立全局常量可以使用export和read-only开关。
$ declare -rx COMPANY_BRANCH=”West Coast Branch”
COMPANY_BRANCH在当前的脚本中是只读变量。当在第二个脚本输出为普通变量时,只读属性会丢失。这种奇怪的现象源自Linux在程序之间共享环境变量,Bash外壳程序本身没有作任何事情。
环境变量是哪些需要在程序之间进行共享的变量。就像洋葱的皮一样,一个程序必须输出它的变量以便下一个程序可以看到这个变量。
虽然Linux已经对输出环境变量提供了些准备,但是没有办法给它们分配属性,Linux也没有要使用这些属性的打算。在环境变量被第一次使用之后,Bash属性才被设计出来。而Bash变量在Linux和一个新的程序共享之后,属性会被丢弃。当第二个脚本开始,它没有办法知道原始的属性是什么。
和一个新程序共享的变量是原始变量的一个复制品,假如脚本声明了一个输出的变量,并运行于第二个脚本中,第二个脚本所作的任何更改对于第一个脚本是不可见的。没有办法能使第二个脚本分配的新值能让第一个脚本看到。和其他程序语言不同,输出脚本变量是一个单行线。
假如有两个程序分别叫outer.sh和inner.sh。outer.sh声明了一个变量并运行在inner.sh脚本中,如列表5.1和5.2所示:
列表 5.1 outer.sh
# outer.sh
#
# This script runs first.
declare -rx COMPANY_BRANCH=”West Coast Branch”
bash inner.sh
printf “%s/n” “$COMPANY_BRANCH”
exit 0
列表5.2 inner.sh
# inner.sh
#
# This script is run by outer.sh.
printf “This is the inner script./n”
declare -p COMPANY_BRANCH
COMPANY_BRANCH=”East Coast Branch”
printf “%s/n” “$COMPANY_BRANCH”
printf “Inner script finished/n”
exit 0
在outer.sh运行时,COMPANY_BRANCH变量是只读的。可是,在inner.sh中,只读属性丢失了,inner.sh将这个属性更改了一个新值,但是inner.sh运行结束后,outer.sh显示了这个变量的值没有改变。
$ bash outer.sh
This is the inner script.
declare -x COMPANY_BRANCH=”West Coast Branch”
East Coast Branch
Inner script finished
West Coast Branch
唯一的办法是将变量的值写入到一个文件中然后在调用程序将这个值从文件中读出写入变量中。
Bash在命令行遇到变量时可以实现变量的替换,在命令执行之前,Bash查看整个命令是否有“$”符号,如果有则执行命令替换。但是,Bash只替换一次,如果要替换的变量包含“$”符号,则这个符号将会保留下来作为普通字符。
$ declare —rx COMPANY=”Value Book Resellers”
$ declare —rx TITLE=’$COMPANY’
$ printf “%s/n” “$TITLE”
$COMPANY
在printf命令执行之前,Bash使用$COMPANY替换TITLE字符。Bash并不替换COMPANY变量,替换操作只执行一次。
有时,你想多执行一次替换,eval命令可以在需要时进行替换操作。执行下面简单的实例,如列表5.3所示,靠什么三个变量中的一个显示屏幕上。
列表 5.3 eval_example.sh
# eval_example.sh
shopt -s -o nounset
declare -r DISPLAY_VARIABLE=’$SALES_EAST’
declare -i SALES_EAST=1000
declare -i SALES_WEST=2000
declare -i SALES_NORTH=3000
printf “DISPLAY_VARIABLE = %s/n” “$DISPLAY_VARIABLE”
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” “$DISPLAY_VARIABLE”
这段脚本可以把三个分支机构中的一个机构的销售数字显示出来,常量DISPLAY_VARIABLE包含了要显示的变量SALES_EAST,SALES_WEST,SALES_NORTH。然而,DISPLAY_VARIABLE没有按想要的东西替换,只是替换了字符串“$SALES_EAST”。
单引号(反引号)运行eval命令会执行一个新的替换。结果替换掉原始的printf命令,这样你的结果就对了。
$ bash eval_example.sh
DISPLAY_VARIABLE = $SALES_EAST
reprocessed with eval, DISPLAY_VARIABLE = 1000
在这个例子中,程序首先执行printf命令:
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” $SALES_EAST’
只有在显示¥SALES_EAST时,Bash才完成变量的替换。当Bash执行eval命令,执行echo命令的第二个检查。
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” ‘ printf “%s/n” 1000’
因为Bash也尝试运行这个结果,echo命令(printf的简化版)是必要的。eval尝试重新评估$DISPLAY_VARIABLE,仿佛他就是一个命令。有了eval 1000,Bash会执行数据1000,这并不是你需要的。
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” 1000
虽然这个例子不是特别有实际意义,但是eval命令在用户输入要执行的命令或从文件中读取命令执行,这就有用了。这个命令可能包含有变量或“$”符号引导的函数等等,就可以使用eval命令来处理它们了。
困难通常不是替换过程的处理,例如:将$SALES_EAST的值分配给DISPLAY_VARIABLE,SALES_EAST被指定,第二个“$”符号加到了printf语句中。
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” “/$$DISPLAY_VARIABLE”
你会得到这样的响应:
reprocessed with eval, DISPLAY_VARIABLE = 14235SALES_EAST
在这个例子中,第一个替换命令执行后的命令如下:
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” $$SALES_EAST’
“$$”命令是一个外壳的内置函数。Bash不清除$SALES_EAST被认为嵌套在第一个“$”符号内。第二个替换执行$$函数,而不是使用SUM变量进行替换。为了正确执行,你必须使用反斜杠来屏蔽第一个“$”符号,将其保留到替换这后。
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” “///$$DISPLAY_VARIABLE”’
在第一次替换后,命令如下:
printf “reprocessed with eval, DISPLAY_VARIABLE = %s/n” /
‘eval printf “%s/n” /$$SUM’
这样就避免了“$$”被认为是内置函数,为了正确替换$SALES_EAST,你必须绕过DISPLAY_VARIABLE内部的“$”符号。
你也可以通过去掉一些引号达到相同的效果。
变量和常量形成了所有脚本的编译块,但是它们只服务于存储容器和表达式,这些将在下一章讨论。
story.bash:一个故事生成器
列表5.4是一个完整的示例,说明了本章的一些概念。
story.bash用来根据用户的选择替换单词。
列表5.4
#!/bin/bash
#
# story.bash: a story generator
#
# Ken O. Burtch
# CVS: $Header$
shopt -s -o nounset
declare NAME # a name
declare COLOR # a color
declare DAY # a day
declare RELATIONSHIP # a person’s relationship
declare OBJECT # an everyday object
declare -a ACTIVITY # a list of everyday activities
# Instructions
printf “%s/n” “This is a story generator. I will ask you for some”
printf “%s/n” “common words. Then I will compose a story.”
printf “/n”
# Read the variables
read -p “Enter a man’s name : “ NAME
read -p “Enter a color (eg. red) : “ COLOR
read -p “Enter a day (eg. Monday) : “ DAY
read -p “Enter a person’s relationship (eg. uncle): “ RELATIONSHIP
read -p “Enter an everyday object (eg. pencil) : “ OBJECT
read -p “Enter an everyday activity (eg. sleeping): “ ACTIVITY[0]
read -p “Enter an everyday activity (eg. reading) : “ ACTIVITY[1]
printf “/n”
read -p “Press return/enter to read your story”
printf “/n”
# Write the story
printf “%s/n” “$DAY at work, $NAME realized that he had forgotten to pack”
printf “%s/n” “a lunch. Ignoring his $SHELL prompt, $NAME decided to head”
printf “%s/n” “out early and grab lunch from a street vendor.”
printf “%s/n” “As he got outside of the front door of the office,”
printf “%s/n” “$NAME ran into his $RELATIONSHIP carrying a”
printf “%s/n” “$COLOR $OBJECT. His $RELATIONSHIP remarked that it had”
printf “%s/n” “been $RANDOM days since $NAME had called. $NAME”
printf “%s/n” “thought he would have been off surfing the net on his”
printf “%s/n” “$OSTYPE computer than running into his $RELATIONSHIP. He”
printf “%s/n” “offered to take the $OBJECT to get polished.” /
“ He went ${ACTIVITY[0]}”
printf “%s/n” “down the street, wishing that his $RELATIONSHIP had stayed”
printf “%s/n” “home ${ACTIVITY[1]} instead.”
exit 0
Declare命令开关
n -a—声明一个数组
n -f—声明一个函数和它的定义
n -F—声明一个函数名
n -r—声明一个制度变量
n -x—声明一个导出的变量
n -I—声明一个整数变量
Bash预定的变量
n auto_resume—If set,allows command completion for the names of stopped jobs.
n BASH—外壳的完整路径
n BASH_ENV—在一个外壳脚本中,在脚本开始执行前显示配置文件名
n BASH_VERSION—Bash的版本
n BASH_VERSINFO—一个数据保存了比BASH_VERSION 更完整的信息。
BASH_VERSINFO[0] 主版本号(the release)。
BASH_VERSINFO[1] 此版本号 (the version)。
BASH_VERSINFO[2] 补丁的级别。
BASH_VERSINFO[3] 编译的版本。
BASH_VERSINFO[4] 发行的状态 (for example,beta1)。
BASH_VERSINFO[5] MACHTYPE的值。
n CDPATH—使用CD命令时,查找根据冒号分隔的目录列表。
n COLUMNS—显示屏每行显示的列数(for example,80).
n COMP_WORDS—在一个程序完成的函数中,当前行单个单词的列表。
n COMP_CWORD—当前的COMP_WORDS。
n COMP_LINE—当前行。
n COMP_POINT—当前命令行的光标位置。
n COMPREPLY—结束返回的列表
n DIRSTACK—使用dirs,popd,and pushd命令的目录列表。
n EUID—当前用户的有效用户ID。
n FCEDIT—使用fc命令的缺省文本编辑器。
n FIGNORE—使用自动补充功能时,命令的后缀或前缀。
n FUNCNAME—在一个函数中,函数的名称。
n GLOBIGNORE—使用路径名扩展时需要忽略的文件名列表(使用冒号分隔模板)。
n GROUPS—本组用户成员列表(使用数字格式)。
n histchars—在命令中用于历史扩展时的字符列表。
n HISTCMD—在命令历史表中当期命令的位置。
n HISTCONTROL—决定空格开头的命令或重复命令是否放入命令列表中。
n HISTFILE—保存已输入命令历史的文件。
n HISTFILESIZE—HISTFILE的大小。
n HISTIGNORE—忽略历史中的特定命令,使用冒号进行分割。
n HISTSIZE—保存在命令历史表中命令的数量(例如,1000).
n HOSTNAME—计算机名、机器名或域名。
n HOSTTYPE—计算机的类型。
n HOME—登陆的主目录
n IGNOREEOF—忽略文件结束字符,并且不退出 shell。如果用户想要退出,则必须键入 exit 命令或按 11 次 Ctrl-D。
n IFS—内部字段分隔符,用于将一行分为几段。外壳把每个 $IFS
字符对待成一个分隔符,且基于这些字符把其他扩展的结果分割。
n INPUTRC—被Readline命令用做启动文件,可作为特定情况下键盘的映射。
n LANG—它的值用于指.定LC_*环境变量没有设置的所有变量值。
n LC_ALL—覆盖所有LC_*和LANG变量。
n LC_COLLATE—控制路径扩展和模板匹配之后的排列顺序。
n LC_CTYPE—控制路径扩展和模板匹配之后的字符分类。
n LC_MESSAGES—指定使用某区域的响应与信息的格式。
n LINENO—在脚本函数中的当前命令的行号。
n LINES—显示器中水平可以显示的行数。 (例如:24).
n MACHTYPE—对计算机的描述,可以是$HOSTTYPE 和$OSTYPE的组合。
n MAIL—对流入的邮件进行检查的文件名,它被MAILPATH延迟。
n MAILCHECK—检查MAILPATH的秒数,缺省为60秒。
n MAILPATH—冒号分隔的文件列表用来检查流入的邮件。
n OSTYPE—操作系统名称。
n OLDPWD—先前的工作目录(由cd命令设置)。
n OPTERR—getopts显示的错误信息。
n PATH—要执行命令的查找目录,由冒号分隔的表。
n PIPESTATUS—每个命令在前一个管道中退出状态值的列表数字。last pipe.
n PPID—外壳父进程ID号。
n PROMPT_COMMAND—在PS1提示符之前要执行的命令。
n PS1—主提示符。
n PS2—次提示符,一条命令没有结束的话在二行给出的提示符。
n PS3—菜单提示符。保存用于select空置结果的菜单提示符。
n PS4—保存bash的调试提示符。
n PWD—当前的工作目录
n RANDOM—返回一个在0和32767之间的一个随机数。
n OPTARG—由getopts命令处理的上一个参数。
n OPTIND—有getopts命令处理的上一个参数的索引值。
n SECONDS—Bash运行以来的秒数。
n SHELL—你采用的外壳。
n SHELLOPTS—一个由冒号分隔的shell已经启用的选项列表。
n SHLVL—在Bash内部,每启动一个新的Bash会话,这个变量值会自动加1。
n TIMEFORMAT—显示时间的格式。
n TMOUT—如果大于零,表示交互式会话的停止时间,缺省为read命令的停止时间。
n UID—当前用户的ID号。
linux的一些发行版还定义了一些额外的变量,这些变量依赖于具体的发行版本。有许多变量还直接用于特定的应用。
n _ETC_PROFILE—如果/etc/profile 文件执行则显示1。
n DISPLAY—X Window显示服务器。
n CVSROOT—CVS库的位置。
n EDITOR—你的缺省编辑器。表示为行编辑器,非可视化编辑器没有出现以前。
n KDEDIR—KDE桌面的父目录。
n HOST—主机的全名。 (例如:host.domain.com)
n INPUTRC—inputrc文件的位置。 (例如:/etc/inputrc)
n LESS—保存less命令的缺省开关。
n LESSOPEN—使用less命令打开文件时使用的缺省命令(例如:|/usr/bin/lesspipe.sh %s)
n LESSCHARSET—使用less命令时的控制台字符集。(例如:latin1)
n LS_COLORS—使用ls命令时的缺省颜色,覆盖了/etc/DIR_COLORS。
n LOGNAME—当前的登录名。
n ORGANIZATION—组织的名字。(通常是/etc/organization的名字)
n PRINTER—缺省打印机。
n QTDIR—含有QT的目录,,包含有KDE桌面的一些部件。
n PAGER—缺省的分页程序,一般用于less命令。
n TEMPDIR—缺省的临时目录的路径。
n TERM—终端模拟器。(例如:xterm会话使用xterm,linux控制台使用linux。)
n USER—OpenLinux显示的用户名。
n VISUAL—缺省编辑器,和EDITOR差不多。
WINDOWMANAGER— 当前 X Windows 窗口管理器的路径。