变量与算数
shell脚本与函数还有位置参数的功能;传统的说法应该是”命令行参数”;
shell为内嵌算数提供了一种标记法,称为算数展开.shell回对$((...))里的算符表达式进行计算,再将计算后的结构放回到命令的文本内.
有两个相似的命令提供变量的管理,一个是readonly,它可以使变量称为只读模式;而赋值给它们是被禁止的.在shell程序中,这是创建符号常量的一个好方法:
days_per_week=7 赋值
readonly days_per_week 设为只读模式
export,readonly
语法:
export name[=word]...
export -p
readonly name[=word]...
readonly =p
用途:
export用于修改或打印环境变量,readonly则使得变量不得修改.
主要选项:
-p:打印命令的名称以及所有被到处(只读)变量的名称与值,这种方式可使得shell重新读取输出以便重新建立环境(只读设置).
行为模式:
使用-p选项,这两条命令都会分别打印他们的名称以及被到处的或只读的所有变量.
较常见的命令是export,用法是将变量放进环境变量里.环境是一个名称与值的简单列表,可供所有执行中的程序使用.新的进程会从父进程继承环境,也可以在建立新的紫禁城之前修改它.export命令可将新变量添加到环境中:
PATH=$PATH:/usr/local/bin 更新PATH
export PATH 导出它
使用export -p命令可以显示当前环境
变量可以添加到程序环境中,但是对shell或接下来的命令不会一直有效:将该变量赋值,置于命令名称与参数前即可:
PATH=/bin:/usr/bin awk ‘...’ file1 file2
这个PATH值只对后面awk起作用,其他命令将使用系统PATH.
使用env命令显示所有环境变量.
unset命令从执行中的shell中删除变量与函数.
案例:
清除环境变量的值使用unset命令.如果未定义指定值,则该变量值将被设为NULL.
首先使用命令export TEST=”test”来增加一个环境变量
接着使用命令env | grep TEST,得到结果TEST=test
然后使用命令unset $TEST 删除环境变量TEST
最后使用命令env | grep TEST命令,该命令不会有输出,说明成功的删除了.
其中unset还可以通过添加-f选项删除指定的函数.
unset的行为模式
如果没有提供选项,则参数将视为变量名称,并告知变量已删
除,如果使用-f选项,参数则被视为函数名称,并删除函数.
注意:myvar=赋值并不会将myvar删除,只不过试讲其设为null字符串.相对的:unset myvar则会完全删除它.这一差异在于”是变量设置”以为”是变量设置,但非null”展开.
参数展开
var =”hello,world”
echo ${var}
hello,world
其实这里说的参数(parameter)不就是我们通常说的变量(variable)么? 嗯。。其实大部分时候这俩名词的意思基本等同。只不过在Shell中parameter(参数)是variable(变量)的超集: 变量名不能以数字开头,而参数名可以。比如说$1就表示命令行传入的第一个参数。
参数展开是shell提供变量值在程序中使用的过程:例如,作为新变量的值,或是作为命令行的部分或全部参数.最简单的形式如下所示:
reminder =”Time to go to the dentist” 将值存储在reminder中
sleep 120 等待两分钟
echo $reminder 显示信息
在shell下,有更复杂的形式可用于更特殊的情况.这些形式都是将变量名称括在花括号里(${variable}),然后再增加额外的语法以告诉shell该做些什么.花括号本身也是很好用的,当你需要在变量名称之后马上跟着一个可能会解释为名称一部分的字符时,他就派上用场了:
reminder =”Time to go to the dentist” 将值存储在reminder中
sleep 120 等待两分钟
echo ${reminder} 显示信息
警告:默认情况下,未定义的变量会展开为null(空的)字符串.程序随便乱写,就可能会导致灾难发生:
rm -rf /$MYPROGRAM 如果未设置MYPROGRAM,就会有大灾难发生了,所以在写 程序时一定要小心.
展开运算符
第一组字符串处理运算符用来测试变量的存在状态,且为在某种情况下的允许默认值的替换.
替换运算符
运算符 |
替换 |
${varname:=word} |
如果varname存在且不是null,则返回它的值;否则,设置它为word,并返回其值 用途:如果变量未定义,则返回默认值. 范例:如果 count未定义,则 echo ${count:-0}的值为0 |
${varname:word} |
如果varname存在且不是null,则返回它的值;否则,设置它为word,并返回其值. 用途:如果变量未定义,则设置变量为默认值. 范例:如果count未定义,echo${count:=0}输出为0 |
${varname:?message} |
如果varname存在且非null,则返回它的值;否则,显示varname:message,并退出当前的命令或脚本.省略message会出现默认信息parameter null or not set.注意,在交互式shell下不需要退出(在不同的shell间会有不同的行为,用户需自行注意). 用途:为了捕捉由于变量未定义所导致的错误. 范例:${count:?”undefined”}将显示:count:undefined!,且如果count未定义,则退出 |
${varname:+word} |
如果varname存在且非null,则返回word;否则,返回null. 用途:未测试变量的存在. 如果:如果count已定义,则${count:+1}返回1(也就是真) |
该表中每个运算符内的冒号(:)都是可选的.如果省略冒号,则将每个定义的”存在且非null”部分改为”存在”,也就是说,运算符仅用于测试变量是否存在.
模式匹配运算符
运算符 |
替换 |
例:${path#/*/} |
结果:tolstoy/mem/long.file.name |
例:${variable##pattern} |
如果模式匹配于变量值的开头处,则删除匹配的最长部分,并返回剩下的部分. |
例:${path##/*/} |
结果:long.file.name |
例:${path%.*} |
结果:/home/tolstoy/mem/long.file |
例:${variable%pattern} |
如果模式匹配于变量值的结尾处,则删除匹配的最短部分,并返回剩下的部分. |
例:${variable%%pattern} |
如果模式匹配于变量值的结尾处,则删除匹配的最长部分,并返回省下的部分 |
例:${path%%.*} |
结果:/home/tolstoy/mem/long |
案例分析:${parameter#word}或{parameter##word}
作用:从parameter头部开始匹配word,并删除成功匹配的部分.在构造word时可以使用”*”表示任意长度的字符,”?”表示单位长度字符,并可用形如”[a-c]”的方式制定匹配”abc”中的任意字符.
另外,”#”和”##”的区别在于前置是匹配最短,而后者是最长匹配;实际上就是正则表达式中的”懒惰”和”贪婪”的概念.
var=br1br2ead
echo ${var$$*br}
输出:2ead
echo ${var#*br}
输出:1br2ead
案例:
${parameter%word}或${parameter%%word}
作用:与前例相似,唯一不同的是从$parameter的为不开始匹配.
var="La.Maison.en.Petits.Cubes.avi"
echo ${var%.*}
输出:La.Maison.en.Petits.Cubes
echo ${var%%.*}
输出:La
分析:匹配案例中的”.”时,shell会从$var的尾部开始查找”.”,如果是最短匹配(echo ${var%.*}),则会找到第一个”.”就停止,否则(echo ${var%%.*})会一直找到最后一个”.”才停止.可以看到,这种用法可以分方便的去掉文件后缀,从而得到文件名.
使用${#variable}可以获得variable的长度:
案例:variable=qwertyuiop;
echo ${#variable}
输出:10
记忆:
#匹配的是前面,因为数字正负号总是置于数字之前;%匹配的是后面,因为百分比符号总是更在数字的后面.
这里用到了两种匹配模式:/*/,匹配任何位于两个斜杠之间的元素;.*,匹配点号之后接着的任何元素.
位置参数
所谓的位置参数,指的是shell脚本的命令行参数;同时也表示zaishell函数内的函数参数,他们的名称是以单个的整数来命名.当整数大于9时,就应该以花括号括起来:
echo frist arg is $1
echo tenth arg is ${10}
也可以将其与模式匹配运算符结合,应用到位置参数:
filename=${1:-/dev/tty} 如果给定参数则使用它,如无参数则使用/dev/tty
接下来的特殊”变量”提供了对传递的参数的总数的访问,以及一次对所有参数的访问:
$# : 提供传递到shell脚本或函数的参数总是.当你是为了处理选项和参数而建立循环时,它会很有用.举例:
while [ $# !=0 ] 以shift逐渐减少$#,循环将会终止
do
case $1 in
... 处理第一个参数
esac
shift 已开第一个参数
done
$*,$@ : 以此表示所有的命令行参数.着两个参数可用来把命令行参数传递给脚本或函数所执行的程序.
“$*” : 将所有命令行参数视为单个字符串.等同于”$1 $2 ...” $IFS的第一个字符用来作为分隔符,衣服个不同的值来建立字符串.案例:
printf “他和arguments were %s\n” “$*”
“$@” : 将所有的命令韩参数视为单独的而个体,就业就单独字符串.等同于”$1” “$2” ....这是将参数传递给其他程序的最佳凡是,因为他会保留所有的内嵌在每个参数里的任何空白.案例:
lpr “$@” 现实每一个文件
shift命令是用来”截去”来自列表的位置参数,由左开始.一旦执行shift,$1的初值会永远消失,取而代之的是$2的旧值.$2的值变成$3的旧值,依次类推.$#的值会逐次减一.shift也可使用一个可选的参数,也就是要位移的参数的计数.单纯的shift等同于shift 1.案例:
set -- hello “hi there” greetings 结束选项部分,自hello开始新的参数
echo $# 显示计数值
for i in $* 循环处理每一个参数
> do echo i is $i
> done
输出:
i is hello
i is hi
i is there
i is greeting
注意,内嵌的空白已消失
使用命令:for i in $@ 在没有双引号的额情况下,$@和$*得到的结果一样
> do echo i is $i
> done
加了双引号for i in “$*” $*表示一个字符串
> do echo i in $i
> done
输出:
i in hello hi there greeting
加了双引号for i in “$@” $@保留真正的参数值
输出:
i in hello
i in hi there
i in greeting
使用命令shift截去第一个参数
echo there are now $# arguments
输出:there are now 2arguments 证明第一个参数已经消失
使用命令:
for i in “$@”
输出为:
i in hi there
i in greeting
特殊变量
POSIX中的内置变量
变量 |
意义 |
# |
表示变量的个数,常用于循环 |
@ |
当前命令行所有参数,置于双引号中,表示个别命令 |
* |
当前命令行所有参数.置于双引号中,表示将命令行所有参数当做一个单独参数 |
-(连字号) |
在引用数给予shell的选项 |
? |
表示上一个命令退出的状态 |
$ |
表示当前进程编号 |
0 |
表示当前进程名称 |
! |
表示最近一个后台命令的进程编号 |
HOME |
表示当前用户的根目录 |
IFS |
表示内部的字符分隔符 |
LANG |
当前locale默认名称 |
PATH |
环境变量 |
PPID |
父进程编号 |
PWD |
当前工作目录 |
特殊变量$$可在编写脚本时用来建立具有唯一性的文件名(多半是临时的),这是根据shell的进程编号建立文件名.不过系统中还有一个mktemp也能做同样的事情.
算数展开
shell的算数运算符与C语言里的差不多,优先级与顺序也相同.
运算符 |
意义 |
顺序 |
++ -- |
增加以减少,可前置也可放在结尾 |
由左至右 |
+ - ! ~ |
一元的正好与符号;逻辑与位的取反 |
由右至左 |
* / % |
乘 除 取余 |
由左至右 |
+ - |
加 减 |
由左至右 |
<< >> |
向左位移,向右位移 |
由左至右 |
< <= > >= |
比较 |
由左至右 |
== != |
相等不相等 |
由左至右 |
& |
位的AND |
由左至右 |
^ |
韦德Exclusive OR |
由左至右 |
| |
位的OR |
由左至右 |
&& |
逻辑的AND |
由左至右 |
|| |
逻辑的OR |
由左至右 |
?: |
条件表达式 |
由右至左 |
= += -= *= /=&= ^= <<= >>= |= |
赋值运算符 |
由右至左 |
该表的运算符的优先级由高排列至最低.
可利用圆括号将子表达式语句括起来.像C一样:关系运算符(<,<=,>,>=,==与!=)产生数字结果中,1为真,0为假.
例如:$((3>2))的值为1;echo $(((3>2)||(4<=1)))也为1,因为着两个子表达式里有一个为真.
对逻辑的AND与OR运算符而言,任何非0值函数都为真:
echo $((3&&4)) 3与4都为”真”
++和--运算符不用说了.++与--运算符是可选的;实际上,所有支持${{...}}的shell,都可以让用户在提供变量名称时,无须前置$符号.