admin 管理员组文章数量: 1184232
2024年3月19日发(作者:ajaxsend)
实践与经验
文章编号:1007—1423(2014)30—0028—04 DOI:10.39696.issn.1007-1423.2014.30.009
小结shell脚本编程注意事项
石庆冬
(英特尔移动通信技术(北京)有限公司,北京100015)
摘要:
Bash是目前UNIX/Linux操作系统中流行的shell之一.它是Bourne shell的超集。Bash简单易学,没有复杂的语法结
构。但与其他编程语言一样,Bash有很多需要编程者注意之处。通过举例,对常见的容易被忽视的地方进行讲解,无
论对于Bash的初学者还是中高级用户都有很好的借鉴意义。
关键词:
Linux shell;Bash;脚本编程;UNIX
O 引言
Linux平台下的shell有很多种.Bash是Linux的缺
省she1l Android是一种以Unux为基础的开源操作系
文件名可以使用短线(也叫减号或连字符),但首
字母不要使用短线。首字母为短线时,短线后面的字母
会被误认为是命令的选项。虽然有办法让命令规避该
误解。但最好的选择是:避免短线出现在文件名之首。
统.随着Android的兴起.需要学习和使用Linux shell
尤其是Bash的人越来越多。Bash是一门脚本语言。像
其他语言一样.Bash也有“陷阱”.需要引起用户的注
意。
2 注意命令退出状态值是否被掩盖
在脚本中.每行只放一条单独的命令。尽量少用组
合命令,否则可能会掩盖命令退出状态值。例如,将命
令date的输出赋给变量var.同时将其定义为只读变
量.如果不小心输入了data,整条命令肯定会不成功。
但如果仅通过查看退出状态¥?的值.却显示整条命令
是成功的:
¥readonly va ̄-¥(data)
bash:data:command not found
1 文件命名
文件名不要使用空格、星号和问号等特殊字符,尽
可能使用字母数字下画线。例如,批量备份文件,可以
对文件名进行循环。假设其中有一个带空格的文件,名
字为name list.txt.当对其进行备份时.看看会发生什
么:
¥foo=”name list.txt”
¥echo¥?
0
¥cp¥foo¥{f0o}—backup
cp:target、list.txt
backup is not a directory
_
命令data失败了.但将变量定义为只读的命令是
成功的。¥?记录的是命令readonly的运行结果。
使用管道的时候注意.整条命令的退出状态决定
于管道之后命令的退出状态。例如,用echo命令显示
一
出错了。将cp命令展开后是:cp name list.txt
name list.txt
backup.该命令试图将名字为name和list.
_
txt的文件复制到目录list.txt_backup里面.这显然不
对。在文件名两边加上引号,即用cp”¥foo…’¥{fool
_
句话.再用管道接Wc命令显示这句话的字节数。如
backup”就可以避免上述错误.但最好的选择是:不在
文件名中使用空格
果将echo中的字母0误输人为数字0.整条命令会失
败.但¥?的值表现为成功:
¥echO”he110 Mike”l WC—c
回 现代计算机2014.10下
bash:echO:command not found
¥echo¥?
O
从以上的例子可知.在脚本中。对组合命令的退出
状态值进行判断。以决定其后的逻辑如何进行时.可能
会掩盖错误
3 充分利用shelI选项.让脚本更健壮
用set ̄lshopt命令可以设置很多选项。弄清楚各
个选项的默认值,并恰当地设置它们,能够使得脚本更
健壮。下面举几个与选项相关的例子。
脚本,其实是一条条的命令排在一起.按照逻辑顺
序先后执行。如果某条命令失败了,根据默认规则,其
后的命令仍可继续运行。这对于用户及时发现问题是
不利的。用命令set—e或者set一0 errexit打开选项er.
rexit之后,某条命令失败了的话,脚本将立刻退出。
Bash允许不声明变量.也没有对变量赋初值而直
接使用变量,不像有的编译语言那样严格。脚本中如果
引用了一个没有赋初值的变量是危险的.虽然在Bash
中对未赋初值的变量可以等同于值为空的变量.但还
是不一样的:有值的变量在变量表里面可以查询到.未
定义的变量是查不到的。用命令set—U或者set—o
n叫nset打开选项n0unset之后.当引用未定义的变量
时,Bash将给出错误提示。例如:
¥set—u #打开选项nounset
¥echo Sx#因为x无初值.提示出错
-
bash:x:unbound v able
用shitf[N】命令左移位置参数时,有时候左移的个
数会大于剩余位置参数的个数,而用户又很难注意到。
看下面这个脚本。显然.它的功能是打印编号为奇数的
脚本参数:
¥cat sfi.sh
#!/bin/bash
whileⅡ¥#>0】]
do
echo¥1
shift 2
done
运行一下试试.虽能打印出编号为奇数的参数.但
却显示了无穷多个a5:
¥sft.sh al a2 a3 a4 a5
a1
a3
a5
a5 #显示无穷个a5
在显示a5之前,参数仅剩1个,¥#等于1,却左移
2个参数(shitf 2),于是发生异常。打开选项shift
_
ver
bose,当左移参数的个数大于剩余的参数个数时,会提
示出错,脚本修改如下:
¥cat sft
_
a.sh
#!/bin/bash
shopt—s shift
_
verbose
while[【¥#>0】]
do
echo¥1
shitf 2
【【¥?!=0】]&&exit 1
done
这时运行脚本,显示完编号为奇数的参数后,Bash
给出了“移动个数超限”的提示,然后脚本正常结束:
¥sft
_
a.sh al a2 a3 a4 a5
a1
a3
a5
.
/sfi
_
a.sh:line 6:shitf:2:shitf count out of range
4 脚本中的命令并非都在同一个子shelI中
一
个脚本可以调用其他脚本,可以使用管道,脚本
中的命令并非都在同一个子shell中。尤其是有变量值
传递时,更需要注意这一点。下面的脚本对文件
name
_
list.txt(假设该文件一共有3行)进行处理,打印
每一行的行号和总行数:
¥cat file
lines.sh
_
#!/bin/bash
count=O
cat name
_
list.txt 1 while read line
do
((count++))
echo”Line¥count”
done
echo”This file has¥count lines”
铜 {+笛如 a^¨-^
国
实践与经验
表面看,脚本没有问题。运行后发现,总行数显示
为0:
¥file
_
lines.sh
Line 1
Line 2
Line 3
Thisfile has 0lines
因为管道之后的while循环运行在新的子shell之
中.不同子shell中的同名变量并不是同一个变量。改
用输入重定向来读取文件.就不存在这样的问题了:
¥cat ifle linesenhance.sh
_
#!/bin/bash
count=0
while read line
do
((count++))
echo”Line¥count”
done<name list.txt
echo”This file has¥count lines”
运行该脚本.可见总行数是正确的:
¥file
_
lines
_
enhance.sh
Line 1
Line 2
iLne 3
This file has 3 lines
5 使用判断命令时的注意事项
使用Bash中括号形式的判断命令时,有时容易忘
记中括号与条件表达式之间需要有空格。Bash脚本中
的每一行都是一条命令.而不是像C语言那样每一行
是一条语句。命令test 8一gt 6用来判断8大于6是否
成立.test与参数即条件表达式8一gt 6之间需要空格
是很容易理解的.一对中括号是命令test的替代,参数
与左右中括号之间需要有空格就容易理解了。缺少空
格时,命令[8一gt 6】运行出错,加上空格就没有问题了:
¥[8一gt 6】
[8:command not f0und
¥【8-gt 6】
在对字符串进行判断时.如果字符串含有空白字
符,一定要用引号括起来,或者用双中括号:
现代计算机2014.10下
¥st[=--”we al'e friends”
¥【一n¥str]
bash:[:too many arguments
¥【一n”¥str”】
¥[【一n¥str】]
在用双中括号来判断两个字符串是否相等时,当
等号右边没有用引号时.右边的字符串会被当作模式
处理.如果出现星号和问号则被当作通配符。例如:
¥a=Wednesday
¥[[Sa=Wed 11&&[[Sa=Wed???day】]
¥echo 57
0
如果想判断a是否等于Wed*.不希望星号被解释
为通配符.将等号右边的字符串加上引号即可。
if cmdl;then cmd2;else cmd3;fi可以用cmdl&&
cmd2 ll cmd3代替.特别当命令cmdl、cmd2和cmd3比
较简短时.很多用户喜欢使用这种替代形式。多数情况
下,这样替代没有问题。但是,如果cmdl成功但cmd2
失败了.1I后面的cmd3就会执行。看来,这两种写法的
效果是不一样的,使用 else结构是严谨的,尽可能不
要使用替代形式。
6 脚本中做好充分的判断
有些命令.特别是用通配符删除多个文件时,一定
要做好充分判断.否则很可能造成无法挽回的后果。例
如,cd dir_foo;1TI1一fr ,如果目录dir_foo不存在,则cd
命令失败.当前目录下面的所有的文件和子目录将被
删除.这会导致灾难性的结果。应该改为cd dir_foo
&&liB—fr}。或者用如下判断:
if cd dir
_
foo;then
1-in—fr
fi
7 注意sed的退出状态值
命令grep用于模式过滤查找,找到了则退出,状态
为0.没找到为非0。用流编辑工具sed也可以模式查
找.或者模式查找并替换,无论找到还是没找到,sed的
退出状态都为0,有语法错误时,为非0。习惯了使用
grep的用户.在学习sed时需要注意这一点。
8 不在函数和脚本中使用别名
如果在一个函数内定义别名,在函数被调用之前.
变短,但函数可以带参数,别名不能带参数。综上所述,
尽可能不要在函数和脚本中使用别名。
别名是不生效的。在一个脚本内使用别名时注意.脚本
内的别名默认不能展开.因为在非交互shell中.选项
aliases t
expand 默认是关闭的。在脚本中先运行shoD
.
9 结语
虽然Bash是--H较简单的脚本语言.但并非没有
“陷阱”。Bash脚本编程当中需要注意的细节还有很多,
—
S expand
_
aliases开启该选项。别名才可以展开。别名
仅仅一篇短文难免挂一漏万。希望Bash的学习者多总
结,多分享心得。
的主要作用就是将长命令缩短.函数也可以将长命令
参考文献:
[1】唐华.Linux操作系统高级教程[M].北京:电子工业出版社,2008
[2]Mike Jonkman.Bash Pifalls[OL].http://mywiki.wooledge.org/BashPifalls/
作者简介:
石庆冬(1971一),男,江苏扬州人,博士,高级软件工程师,从事领域为Linux下的软件开发与集成工作
收稿日期:2014—09—26 修稿13期:2014—09—26
Summary of shell Script PrOgramming Notes
SHI Qing-dong
(Intel Mobile Communications Technology(Bering)Ltd.,Beijing 100015)
Absttact:
Bash is one of the popular shell scripts on UNIX/Linux operating system at present,it is a superset of Bourne sheH.Bash is simple and
easy to learn,the grammar is not complicated.But as other programming languages,Bash programmers need to pay attention a lot.By
several examples,explains some aspects which are easily overlooked,provides very good summary for both Bash beginners and advanced
users.
Keywords:
Linux shell;Bash;Script Programming;UNIX
硼 *笛加 on1^1n
版权声明:本文标题:小结shell脚本编程注意事项 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1710840546a575746.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论