E w+^Ipl16573151Testing软件测试网x6g3rq'Z$E1.
Linux 脚本编写基础
^1T0CO^_;i1657311.1 语法基本介绍
51Testing软件测试网)S4lk a*z YI'b9X/X1.1.1 开头
51Testing软件测试网 |5}^$ws:i;Im y 程序必须以下面的行开始(必须放在文件的第一行):
51Testing软件测试网](J:a&c j,qH #!/bin/sh
ncC S[u*y5`-O W165731 符号#!用来告诉
系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。 当编辑好脚本时,如果要执行该脚本,还必须使其可执行。
?WJZ"Q8Y?~165731 要使脚本可执行: 编译 chmod +x filename 这样才能用./filename 来运行
51Testing软件测试网 UB%tl-c2H)V"k1.1.2 注释
51Testing软件测试网S,N%?RF&N7G b 在进行
shell编程时,以#开头的句子表示注释,直到这一行的结束。我们真诚地建议您在程序中使用注释。如果您使用了注释,那么即使相当长的时间内没有使用该脚本,您也能在很短的时间内明白该脚本的作用及
工作原理。
51Testing软件测试网"uJP/C:?:vI a6nMsI1.1.3 变量
51Testing软件测试网CB T1H5j r ty 在
其他编程语言中您必须使用变量。在shell编程中,所有的变量都由字符串组成,并且您不需要对变量进行声明。要赋值给一个变量,您可以这样写:
Z7ZD!C%v[Tt `165731 #!/bin/sh
51Testing软件测试网-jB#t0P8CJj #对变量赋值:
51Testing软件测试网D`*z7o,D X/z a="hello world"
:]g$K~7Ms;ecU165731 # 现在打印变量a的内容:
51Testing软件测试网$jd W;OW echo "A is:"
51Testing软件测试网2x(r5A d"q [ ~i echo $a
51Testing软件测试网(ap&| {Z 有时候变量名很容易与其他文字混淆,比如:
n3gf'|oE#S165731 num=2
51Testing软件测试网$kfk1AY echo "this is the $numnd"
;z3_5|6\En165731 这并不会打印出"this is the 2nd",而仅仅打印"this is the ",因为shell会去搜索变量numnd的值,但是这个变量时没有值的。可以使用花括号来告诉shell我们要打印的是num变量:
51Testing软件测试网,y!Qs R:H9L/{ num=2
51Testing软件测试网CT,^g#y echo "this is the ${num}nd"
51Testing软件测试网.^ n#P2^;?0q 这将打印: this is the 2nd
51Testing软件测试网8F na^1Rf1.1.4 环境变量
`|%G3oW#Rx)F165731 由export关键字处理过的变量叫做环境变量。我们不对环境变量进行讨论,因为通常情况下仅仅在登录
51Testing软件测试网 A0f S;D{;d脚本中使用环境变量。
51Testing软件测试网\ BM!s;`@1.1.5 Shell命令和流程控制
6? kD$c8\-_5j*K+h165731 在shell脚本中可以使用三类命令:
51Testing软件测试网z0jr6SIg$m 1)Unix 命令:
8pDQe }8}BC165731 虽然在shell脚本中可以使用任意的unix命令,但是还是由一些相对更常用的命令。这些命令通常是用来进行文件和文字操作的。
51Testing软件测试网/m|&@M Q;as 常用命令语法及功能
51Testing软件测试网Q l\5e:SS&kw9y-G echo "some text": 将文字内容打印在屏幕上
51Testing软件测试网9Qb1I k t ls: 文件列表
51Testing软件测试网x6JQ E_ wc –l filewc -w filewc -c file: 计算文件行数计算文件中的单词数计算文件中的字符数
51Testing软件测试网"E c}+s%QV?ds&T!f cp sourcefile destfile: 文件拷贝
-t @:lU6V'K n(j.n165731 mv oldname newname : 重命名文件或移动文件
Fu.nv2^/U8PEi]0p165731 rm file: 删除文件
51Testing软件测试网'w5t3c2j+b@ d grep 'pattern' file: 在文件内搜索字符串比如:grep 'searchstring' file.txt
51Testing软件测试网&o8g Ft8R&}o1j cut -b colnum file: 指定欲显示的文件内容范围,并将它们输出到标准输出设备比如:输出每行第5个到第9个字符cut -b5-9 file.txt千万不要和cat命令混淆,这是两个完全不同的命令
j5zZ HeH#VdbH165731 cat file.txt: 输出文件内容到标准输出设备(屏幕)上
51Testing软件测试网)AtC D@S*ZSy^ file somefile: 得到文件类型
s b?^Dv O#q1X165731 read var: 提示用户输入,并将输入赋值给变量
51Testing软件测试网6`IP8LUk)P#zB sort file.txt: 对file.txt文件中的行进行排序
51Testing软件测试网)H'U$aX$P+c*R F)WF2T uniq: 删除文本文件中出现的行列比如: sort file.txt | uniq
51Testing软件测试网!pE0}4u'M| expr: 进行数学运算Example: add 2 and 3expr 2 "+" 3
.N3G9j@&q$f.A165731 find: 搜索文件比如:根据文件名搜索find . -name filename -print
"]5Z!}xZ3O \165731 tee: 将数据输出到标准输出设备(屏幕) 和文件比如:somecommand | tee outfile
51Testing软件测试网9]R$V)ax} basename file: 返回不包含路径的文件名比如: basename /bin/tux将返回 tux
v1zHfI6{)x165731 dirname file: 返回文件所在路径比如:dirname /bin/tux将返回 /bin
51Testing软件测试网^G s0cgt)h head file: 打印文本文件开头几行
R/xfC3?7`T165731 tail file : 打印文本文件末尾几行
fBvQX2Wvf165731 sed: Sed是一个基本的查找替换程序。可以从标准输入(比如命令管道)读入文本,并将结果输出到标准输出(屏幕)。该命令采用正则表达式(见参考)进行搜索。不要和shell中的通配符相混淆。比如:将linuxfocus 替换为LinuxFocus :cat text.file | sed 's/linuxfocus/LinuxFocus/' > newtext.file
LmFsy1Q165731 awk: awk 用来从文本文件中提取字段。缺省地,字段分割符是空格,可以使用-F指定其他分割符。
51Testing软件测试网]Q5h"p Ccat file.txt | awk -F, '{print $1 "," $3 }'这里我们使用,作为字段分割符,同时打印第一个和第三个字段。如果该文件内容如下: Adam Bor, 34, IndiaKerry Miller, 22, USA
+a5{F5@4j"b4cX&|:p165731 命令输出结果为:Adam Bor, IndiaKerry Miller, USA
t]_4| i5}9F165731 2) 概念: 管道, 重定向和 backtick
I&JS2uJ"L0C @165731 这些不是系统命令,但是他们真的很重要。
:jT0prMst!\165731 管道 (|) 将一个命令的输出作为另外一个命令的输入。
Yd w,Z ~ r O165731 grep "hello" file.txt | wc -l
51Testing软件测试网6R G9F#FVMcj P 在file.txt中搜索包含有”hello”的行并计算其行数。
ng"V o;B4v,o1Xx165731 在这里grep命令的输出作为wc命令的输入。当然您可以使用多个命令。
51Testing软件测试网[Lf]*h;oq 重定向:将命令的结果输出到文件,而不是标准输出(屏幕)。
51Testing软件测试网9N!MH`+Q > 写入文件并覆盖旧文件
_+g0`4fH$h:VJ:[1x165731 >> 加到文件的尾部,保留旧文件内容。
)aZ2O:PRG3q165731 反短斜线
1fu8l-DVV+e2TNt165731 使用反短斜线可以将一个命令的输出作为另外一个命令的一个命令行参数。
j YN#U\&M165731 命令:
1S4}*k;wg165731 find . -mtime -1 -type f -print
51Testing软件测试网.r&qp;Vec)u7x O.AE 用来查找过去24小时(-mtime –2则表示过去48小时)内修改过的文件。如果您想将所有查找到的文件打一个包,则可以使用以下脚本:
{[k^ P7C S'N!FY165731 #!/bin/sh
x+O0L X%Q1Zp#JG165731 # The ticks are backticks (`) not normal quotes ('):
\@ KL%c$K6vv y/P/X2P165731 tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`
51Testing软件测试网(? W\D6h U 3) 流程控制
51Testing软件测试网 TE0ylH\+?-] 1.if
"N6Lv,i"S[165731 "if" 表达式 如果条件为真则执行then后面的部分:
51Testing软件测试网/gB8a2Vn"zH if ....; then
51Testing软件测试网 v R3U@.C;{6Ml D ....
51Testing软件测试网un6\:v3kY&o7{!w elif ....; then
51Testing软件测试网AF%NW5U ....
51Testing软件测试网k%X}:KZiQ]G1n else
QK&r6R?Z165731 ....
jn,cv;j165731 fi
51Testing软件测试网wX9P0soF;q 大多数情况下,可以使用
测试命令来对条件进行测试。比如可以比较字符串、判断文件是否存在及是否可读等等…
51Testing软件测试网9\$nC D7` G 通常用" [ ] "来表示条件测试。注意这里的空格很重要。要确保方括号的空格。
51Testing软件测试网lxx O/u/Ka [ -f "somefile" ] :判断是否是一个文件
51Testing软件测试网4UE0RJ(~0X'|~ [ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限
51Testing软件测试网-j+O,lmT [ -n "$var" ] :判断$var变量是否有值
51Testing软件测试网|j$^ Jv(uiX,g [ "$a" = "$b" ] :判断$a和$b是否相等
O-bL!a}"J \165731 执行man
test可以查看所有测试表达式可以比较和判断的类型。
"Xq1JZ'ZU%Ft&GM P W165731 直接执行以下脚本:
51Testing软件测试网kK6mE+H2|.vK #!/bin/sh
2G*Qs Uc,Epsfc#t165731 if [ "$SHELL" = "/bin/bash" ]; then
v.Q*aCTj165731 echo "your login shell is the bash (bourne again shell)"
51Testing软件测试网B!e^ v\+N e4Qq else
!Wjw5o"cym g]6Jm165731 echo "your login shell is not bash but $SHELL"
51Testing软件测试网 g:wc%ECq C2{$X fi
Aa1Kx fa L165731 变量$SHELL包含了登录shell的名称,我们和/bin/bash进行了比较。
/QNplQ z$A ]jt165731 51Testing软件测试网!I1g*G zU0?^'cv 快捷操作符
D0}.f;N D.z!e165731 熟悉C语言的朋友可能会很喜欢下面的表达式:
51Testing软件测试网#}^9W}v!D [ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"
j(~{^^s%j165731 这里 && 就是一个快捷操作符,如果左边的表达式为真则执行右边的语句。您也可以认为是逻辑运算中的与操作。上例中表示如果/etc/shadow文件存在则打印” This computer uses shadow passwors”。同样或操作(||)在shell编程中也是可用的。这里有个例子:
51Testing软件测试网h*V_B8`ko gS)H #!/bin/sh
qGp?TZ9m6p%w?165731 mailfolder=/var/spool/mail/james
0Q(o%j"~7or165731 [ -r "$mailfolder" ]' '{ echo "Can not read $mailfolder" ; exit 1; }
9X'^,HY?165731 echo "$mailfolder has mail from:"
F7fn;vksx v;X165731 grep "^From " $mailfolder
51Testing软件测试网9[&ZcS)VVr+W$^H]$B$W 该脚本首先判断mailfolder是否可读。如果可读则打印该文件中的"From" 一行。如果不可读则或操作生效,打印错误信息后脚本退出。这里有个问题,那就是我们必须有两个命令:
51Testing软件测试网3@%J| G!R U -打印错误信息
51Testing软件测试网v9w/S t/O.Y6G%?Z -退出程序
4j-B*v@9uyO)W165731 我们使用花括号以匿名函数的形式将两个命令放到一起作为一个命令使用。一般函数将在下文提及。
51Testing软件测试网x b7Iqm0o(w g 不用与和或操作符,我们也可以用if表达式作任何事情,但是使用与或操作符会更便利很多。
51Testing软件测试网Mycc+T0@#cla C(}51Testing软件测试网.Lb[0dZ$_!G 2.case
51Testing软件测试网 UrG5} qS3lv case :表达式可以用来匹配一个给定的字符串,而不是数字。
6J!?6H5n:q{9c W(`165731 case ... in
h h+k0e-J165731 ...) do something here ;;
m {xQ{/s v P3m165731 esac
L%n]8K5wwLh}165731 让我们看一个例子。 file命令可以辨别出一个给定文件的文件类型,比如:
&X;[G6P'B165731 file lf.gz
hp0PS'Un@165731 这将返回:
51Testing软件测试网 d"A&_1I4o lf.gz: gzip compressed data, deflated, original filename,
51Testing软件测试网/H-ikt;qJH/I last modified: Mon Aug 27 23:09:18 2001, os: Unix
9G-r0F\L Q a6LUF165731 我们利用这一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzip 和zip 类型的压缩文件:
51Testing软件测试网4o.Yq4y!Z #!/bin/sh
q^_/o/@kJi)i4y165731 ftype=`file "$1"`
;Fo|3Km0hX165731 case "$ftype" in
5[F*x }$C165731 "$1: Zip archive"*)
T } h&v$e-I"{t165731 unzip "$1" ;;
51Testing软件测试网Z\Iiw$]9Lil3Q!}!O "$1: gzip compressed"*)
51Testing软件测试网!Kq$@!R&b6rw} gunzip "$1" ;;
51Testing软件测试网o;~2bL}8OS "$1: bzip2 compressed"*)
51Testing软件测试网x;Dp}suB bunzip2 "$1" ;;
M8Q9?2? thm(c_d+_5k165731 *) echo "File $1 can not be uncompressed with smartzip";;
51Testing软件测试网MHW1I5}{Rt1n,X esac
51Testing软件测试网rZ*{0?@.] 您可能注意到我们在这里使用了一个特殊的变量$1。该变量包含了传递给该程序的第一个参数值。
51Testing软件测试网 O0} U,Rp也就是说,当我们运行:
51Testing软件测试网"Q#j8w n%|V?V^ smartzip articles.zip
51Testing软件测试网 m{ F pk g @ $1 就是字符串 articles.zip
.F M~#}gg.yL&c4z!e165731 3. selsect
M:RD7VoTOnL:h165731 select 表达式是一种bash的扩展应用,尤其擅长于交互式使用。用户可以从一组不同的值中进行选择。
K~:o+{ fq z3B165731 select var in ... ; do
#U-d]jf5w)W-?w165731 break
a'iy/u)G;u"d ^`n165731 done
51Testing软件测试网!dqC.IJp .... now $ var can be used ....
6Hn#z`lIh6n fa|165731 下面是一个例子:
51Testing软件测试网N%Y4\ I Q#J3ev)Q #!/bin/sh
K.CF]4v;D F*{7o165731 echo "What is your favourite OS?"
51Testing软件测试网'F,ddZrp7jR select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
^7` c1ygjGF1i)C165731 break
51Testing软件测试网b"a H(b?{ j HG done
51Testing软件测试网X1o] [crq9G}-U echo "You have selected $var"
51Testing软件测试网6rU+I @fuqR(P 下面是该脚本运行的结果:
5sC&|HP165731 What is your favourite OS?
V1t?Ziwh165731 1) Linux
51Testing软件测试网6m/r \;x/S(h?,@ 2) Gnu Hurd
51Testing软件测试网;`&m[;?a,L` 3) Free BSD
^5d!q~,~1J)H"bv165731 4) Other
51Testing软件测试网m?X }+R3_A #? 1
:UN-J*^S5@%`k165731 You have selected Linux
51Testing软件测试网%ftS%B-z5y+uG 注:var是个变量,可以换成
其它的值。break用来跳出循环,如果没有break则一直循环下去。done与select对应。
(m{n(S xZ;kF8B165731 4.loop
51Testing软件测试网u|ea%i loop表达式:
%JQ\0l GQ165731 while ...; do
}\J'lj5q&kag(K!\165731 ....
51Testing软件测试网{+t'S_;{#Ppa done
51Testing软件测试网/D8}V[X)q while-loop 将运行直到表达式测试为真。will run while the expression that we test for is true.
51Testing软件测试网1Rbo)K$U(y9z关键字"break" 用来跳出循环。而关键字”continue”用来不执行余下的部分而直接跳到下一个循环。
51Testing软件测试网s4e+T4P)C/O 51Testing软件测试网*Vs1|0} i;u$v~;{)l for-loop表达式查看一个字符串列表 (字符串用空格分隔) 然后将其赋给一个变量:
9Up}qj wnE0m165731 1, for var in ....; do
51Testing软件测试网 tio'}})TMi ....
At)k x3dY0w165731 done
51Testing软件测试网/oM5f|VC 在下面的例子中,将分别打印ABC到屏幕上:
51Testing软件测试网{'Kimi)C3Q #!/bin/sh
H6j/k]&}1z165731 for var in A B C ; do
51Testing软件测试网~ [ _;o7LK~ echo "var is $var"
51Testing软件测试网)m4taT'Bu'y ]Ib done
+X)o!hX.i+L16573151Testing软件测试网X:{Z,e;U 2, for (( 条件一; 条件二; 条件三 );do
/\f'n9W7mdM165731 ...
-q8W [0aj165731 done
:uiZ:Z9i(tg}\165731 例:
#] EO{#r$a:j H165731 51Testing软件测试网5d$b qW6q6tr for ((i=1;i<10;i=$[$i+1]));do
51Testing软件测试网Hkj&}+c_*M4m@ K9Zz165731 echo "a"
g%_)W m4u YH9q{165731 done
51Testing软件测试网8P'n%i9f |ijK输出:
$n1X,_+U*Er cn165731OH_8mi5M-P E v?165731a
~M[F q/\165731a
;T7sp8P1IWV/F&_165731a
"K*^;^:OW4jvX165731a
v3E[r/f.[s(~6f165731a
9jW/V+o/X165731a
51Testing软件测试网/rt+](| W m(PU-m'Sa
4]`h@9Y0o165731a
51Testing软件测试网 iRi:`,oQ-ha
51Testing软件测试网]i*e~8q3U*YR$_'Bn;L0\2sa1g R165731条件一:这可以看成是『初始值』,如上面的例子中,初始值是 i=1 啦!
51Testing软件测试网HpOK F!YVE|"tp条件二:这可以看成是『符合值』,如上面的例子中,当 i<=100 的时候都是符合条件的!
gt5hO1c#w.wB165731条件三:这可以看成是『步阶』!也就是说, i 每次都加一! 所以啦!上面的例子是说:由 i=1 开始到 i<= 100 ,每次 i 都加一来执行底下的程序段(就是 s=s+i ),当 i >100 (也就是 i=101 )就跳出这一段程序段!怎样!不难吧!
51Testing软件测试网8y%CvN9gJ51Testing软件测试网.aXwaoA Z0uX5b4h51Testing软件测试网9L&C T F8hCG0Z1O*e$l$U-M:hBi:J;s1s |1657312Mf@fA:Q165731 下面是一个更为有用的脚本showrpm,其功能是打印一些RPM包的统计信息:
51Testing软件测试网yeUp/s:E8_*j*g9YAc #!/bin/sh
51Testing软件测试网bh;IQ,Y`$P'|-G&g t # list a content summary of a number of RPM packages
OC]l rS`g165731 # USAGE: showrpm rpmfile1 rpmfile2 ...
51Testing软件测试网 E2T f$M N'v\/v:r"a'u # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
M [_kG1q [.gYE-D165731 for rpmpackage in $*; do
51Testing软件测试网!P3?/f*[|z ?*R if [ -r "$rpmpackage" ];then
)R/\_~7G4t5{k165731 echo "=============== $rpmpackage =============="
Z T;V)\ mA165731 rpm -qi -p $rpmpackage
}nfV U!k165731 else
51Testing软件测试网sNp9LY+x;ug.B^ echo "ERROR: cannot read file $rpmpackage"
51Testing软件测试网 YY u'~%bm$\3A fi
51Testing软件测试网f$nd*n5se3`.j done
51Testing软件测试网H,Ev,`.v2^ 这里出现了第二个特殊的变量$*,该变量包含了所有输入的命令行参数值。
2y.h0v%_L}165731 如果您运行showrpm openssh.rpm w3m.rpm webgrep.rpm
.vqJ'wpbLMd"N165731 此时 $* 包含了 3 个字符串,即openssh.rpm, w3m.rpm and webgrep.rpm.
`/^(SJ;cIB.L9X165731j&o n {5rfU6y/@Q16573151Testing软件测试网/L4h[V&^d:Ybv#\3hG.lk u(bHzo0c.g165731『until:直到条件相同的时候才离开程序』;
51Testing软件测试网8T1g8_6Z5bc"\6v5Li『while:当条件相同的时候,就继续做!』
51Testing软件测试网%Pf)la[ {V/Syov~i,f ?165731until [ condition1 ] && { || } [ condition2 ] ...
u4R1p5K4]B6u165731L w,o p zF0G'[165731%{-qKcdI(\16573151Testing软件测试网 CbCa9@51Testing软件测试网p4LZ%p"z+J3H 5. 引号
U3[0[[kK T165731 在向程序传递任何参数之前,程序会扩展通配符和变量。这里所谓扩展的意思是程序会把通配符(比如*)替换成合适的文件名,它变量替换成变量值。为了防止程序作这种替换,您可以使用引号:让我们来看一个例子,假设在当前目录下有一些文件,两个jpg文件, mail.jpg 和tux.jpg。
51Testing软件测试网)J _D]%cS"J#w&l 编译SHELL脚本
J {bRK`*? qT165731 #ch#!/bin/sh mod +x filename
51Testing软件测试网J-G6[3z8i7x cho *.jpg ./filename 来执行您的脚本。
51Testing软件测试网j.r:fi l+NV.I.X;{ 这将打印出"mail.jpg tux.jpg"的结果。
G'a3_y-E-\},vE{C165731 引号 (单引号和双引号) 将防止这种通配符扩展:
'e7UW#U\3e/Yuh&X165731 #!/bin/sh
nu2jT I"hz9g165731 echo "*.jpg"
51Testing软件测试网&?T.{? @4[H-iN ] echo '*.jpg'
51Testing软件测试网LK{sk,g*^ 这将打印"*.jpg" 两次。
K^S*l9Hz;Lm%?165731 单引号更严格一些。它可以防止任何变量扩展。双引号可以防止通配符扩展但允许变量扩展。
51Testing软件测试网Ytsv-K #!/bin/sh
zg#Vf"mLA0S6yu165731 echo $SHELL
$A.|1B:M6j8i'EC9?o:_165731 echo "$SHELL"
51Testing软件测试网cBV%E~ echo '$SHELL'
51Testing软件测试网RJ(P!{ E}c 运行结果为:
51Testing软件测试网(S$c ^W O)v? /bin/bash
51Testing软件测试网|;p!FK-g(_"K4r| D /bin/bash
6c,U(P1?p165731 $SHELL
51Testing软件测试网M8oP:ukC 最后,还有一种防止这种扩展的方法,那就是使用转义字符——反斜杆:
j_LG^8l5h!`"Ru165731 echo *.jpg
xL6r%z.Xf165731 echo $SHELL
!E6Q5lHX*H|165731 这将输出:
#x5i J K0}165731 *.jpg
51Testing软件测试网s!M&U j&d $SHELL
cjy?/Ju.s}N165731 6. Here documents
:_^f.J/mm/[165731 当要将几行文字传递给一个命令时,here documents(译者注:目前还没有见到过对该词适合的翻译)一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个 here documents就不必用echo函数一行行输出。 一个 "Here document" 以 << 开头,后面接上一个字符串,这个字符串还必须出现在here document的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且使用here documents打印帮助:
:L~1bJ3MN165731 #!/bin/sh
3c^0Z(k!N9d4c165731 # we have less than 3 arguments. Print the help text:
|k `!R G165731 if [ $# -lt 3 ] ; then
51Testing软件测试网G+B vf8E8\ cat <
51Testing软件测试网z'}#ed/|4d;]giW ren -- renames a number of files using sed regular expressions
51Testing软件测试网vPD"Y*Y'e USAGE: ren 'regexp' 'replacement' files...
51Testing软件测试网H0Z+h.J5K EXAMPLE: rename all *.HTM files in *.html:
51Testing软件测试网 J#@|;n\W9V ren 'HTM$' 'html' *.HTM
51Testing软件测试网3`V-NJoVgw HELP
51Testing软件测试网J`7j M0i5o'j exit 0
51Testing软件测试网)T'd$_*u-nf*S.uS fi
"Ax_ pry KXl165731 OLD="$1"
&V$CD#I1trwZM165731 NEW="$2"
51Testing软件测试网Go4qt(})Y)a2Rq7g # The shift command removes one argument from the list of
yQ#JY v%d[5l165731 # command line arguments.
X6\1m4pL8U\4W165731 shift
TWfg"r165731 shift
.X`;`Yuz$D%N165731 # $* contains now all the files:
JuQ0bNx|165731 for file in $*; do
+s6OV-u/r`X%h(W l165731 if [ -f "$file" ] ; then
51Testing软件测试网9uU5A,P+d1M7V"N newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`
o\:t6R%gl H4z165731 if [ -f "$newfile" ]; then
Q(LX)dK+u165731 echo "ERROR: $newfile exists already"
ft%Fn Hb@165731 else
B4ZYxP`l165731 echo "renaming $file to $newfile ..."
4J1l P8yKs165731 mv "$file" "$newfile"
5s5pK4VvZ165731 fi
m']b(Q%P!p8Q~i165731 fi
K"Dp {/p[165731 done
[WR6|!h%eq#]i4zh7K165731 这是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是否小于3个 (特殊变量$# 表示包含参数的个数) 。如果输入参数小于3个,则将帮助文字传递给cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。如果输入参数等于或大于3个,我们就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。下一步,我们使用shift命令将第一个和第二个参数从参数列表中删除,这样原来的第三个参数就成为参数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。接着我们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目的:得到了旧文件名和新
&J I'[D1S165731文件名。然后使用mv命令进行重命名。
51Testing软件测试网P4|;U"Md 4)函数
#MN }"KQ'L165731 如果您写了一些稍微复杂一些的程序,您就会发现在程序中可能在几个地方使用了相同的代码,并且您也会发现,如果我们使用了函数,会方便很多。一个函数是这个样子的:
51Testing软件测试网*@~9L o,T K functionname()
p:y7i%k1yB \O165731 {
51Testing软件测试网,l+N}xI7?0@!\Y5A{ # inside the body $1 is the first argument given to the function
3Fae j-F165731 # $2 the second ...
.dNf7@/z8L(J&sC&t165731 body
51Testing软件测试网]r2B:O~ o1y }
Qs6`j @v(a165731 您需要在每个程序的开始对函数进行声明。
gRK'SII2z C165731 下面是一个叫做xtitlebar的脚本,使用这个脚本您可以改变终端窗口的名称。
)I^ [AY%x165731 这里使用了一个叫做help的函数。正如您可以看到的那样,这个定义的函数被使用了两次。
;yI q8` Or1B]k165731 #!/bin/sh
51Testing软件测试网P X"M9dI(u\ # vim: set sw=4 ts=4 et:
v ?@?r:]8g@165731 help()
51Testing软件测试网g*H4Dh g6T| {
t3{pdl~S X~b,p165731 cat <
51Testing软件测试网t.V}6C {$^:^} QV$Z xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole
G2z)?%qg165731 USAGE: xtitlebar [-h] "string_for_titelbar"
51Testing软件测试网6_N,}2j:W,Wg OPTIONS: -h help text
51Testing软件测试网9Q t;_e6W8wQ)~ EXAMPLE: xtitlebar "cvs"
e V:w"GQ;\fp Z0z(yyhF165731 HELP
M;hc;kCB['ap-W165731 exit 0
S'\U] ZpA165731 }
51Testing软件测试网d,h'K0b eq#S4S0|n3o6{ # in case of error or if -h is given we call the function help:
-UUBks165731 [ -z "$1" ] && help
51Testing软件测试网&hcF[$xZ9_q [ "$1" = "-h" ] && help
4e _:Nsx1W+^m H J165731 # send the escape sequence to change the xterm titelbar:
YY.PesO6^165731 echo -e "33]0;$107"
51Testing软件测试网l} z"W&R`nA #
51Testing软件测试网:s Nm"pV+s['n 在脚本中提供帮助是一种很好的编程习惯,这样方便其他用户(和您)使用和理解脚本。
51Testing软件测试网Z4_1w,VU 命令行参数
51Testing软件测试网G C;^A7Jf:[a 我们已经见过$* 和 $1, $2 ... $9 等特殊变量,这些特殊变量包含了用户从命令行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的参数和查看帮助的-h选项)。但是在编写更复杂的程序时,您可能会发现您需要更多的自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值 (比如文件名)。有好多方法可以实现对输入参数的分析,但是下面的使用case表达式的例子无遗是一个不错的方法。
51Testing软件测试网Q];M2F O#HJ(z(j0P #!/bin/sh
51Testing软件测试网?Gn7iOL"{ h z help()
51Testing软件测试网 \$i0UcZ0bPE {
1I/A vB(Q7A9f165731 cat <
51Testing软件测试网ds{)S_y.H This is a generic command line parser demo.
lyNmgp165731 USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2
51Testing软件测试网,J"X]x+S.y"u%_(D3g HELP
VK%r M c"Qs165731 exit 0
51Testing软件测试网PO,oBz"_:^ }
51Testing软件测试网.SF1c c6d6M\ C while [ -n "$1" ]; do
n da"G}l/M165731 case $1 in
51Testing软件测试网#nOy|1kq+H -h) help;shift 1;; # function help is called
51Testing软件测试网n_)lzV~*LC@a1s$] -f) opt_f=1;shift 1;; # variable opt_f is set
51Testing软件测试网7NXA"l m b -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2
51Testing软件测试网Q"h5Wy4G --) shift;break;; # end of options
7i-F-rUS8|-Y*H165731 -*) echo "error: no such option $1. -h for help";exit 1;;
@.G$VD[;H7T165731 *) break;;
51Testing软件测试网;v$D7[.M0d`'y%r!j esac
3uDIV[y165731 done
i}wAa-s"y&[K8I165731 echo "opt_f is $opt_f"
p_ g$Y*z9U#_W U165731 echo "opt_l is $opt_l"
51Testing软件测试网hC } Z8jG:b6G e echo "first arg is $1"
51Testing软件测试网 n T sw:pb4f2rG echo "2nd arg is $2"
51Testing软件测试网*PK0m2`1f([-I#@Ku 您可以这样运行该脚本:
51Testing软件测试网9U2l_Y#W!gf9wT-} cmdparser -l hello -f -- -somefile1 somefile2
&B@we4T165731 返回的结果是:
51Testing软件测试网p'|9Xji6N opt_f is 1
51Testing软件测试网T H8iV.]*FU opt_l is hello
51Testing软件测试网 Uq[ KM,u.X first arg is -somefile1
51Testing软件测试网9]5E/x_ z+lK 2nd arg is somefile2
\$g2}5_*BGuj165731 这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,首先输入的应该是包含减号的参数.
51Testing软件测试网$T h6{.?i N9keJ51Testing软件测试网o _:dDug8R.qE,yb5N tp`-e&WT165731第2部分 实例
51Testing软件测试网^k-l^;r|d0JMXp,[;IC/@[b165731 现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。并且写一个伪脚本(framework.sh),该脚本包含了大多数脚本都需要的框架结构,是一个非常不错的主意。这时候,在写一个新的脚本时我们只需要执行一下copy命令:
LK!f@r8Y&CA|h165731cp framework.sh myscript.
FI Un#[,O_165731 然后再插入自己的函数。
51Testing软件测试网$]eO@#_|M 让我们再看两个例子:
&ew \s!He-U$ik.a5YAb165731 二进制到十进制的转换
5t F"g_Rz9Gx165731 脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子:
51Testing软件测试网j)? e` A Z#!/bin/sh
|1u @$G.V ^t165731# vim: set sw=4 ts=4 et:
;V}!}p5J*A-Fco165731help()
51Testing软件测试网 Y7zf3PQPjO{
51Testing软件测试网|2o.d DTn cat <
-mO'uJazi&VMd165731b2h -- convert binary to decimal
]k:b$~h165731USAGE: b2h [-h] binarynum
51Testing软件测试网8u$rcx }BOPTIONS: -h help text
%s{G-F'U*p I1Y8M165731EXAMPLE: b2h 111010
51Testing软件测试网J5}6R%BR1xizwill return 58
51Testing软件测试网S4~.uu#] zWHELP
3a*OD'{Y(~8C"eXZ165731 exit 0
0HL*n'@RNlE_Z\165731}
2Zu2^3Emhe165731error()
A w!O4n0br'TX165731{
51Testing软件测试网 `la$O.rzHY # print an error and exit
aM5O+USSLzB S165731 echo "$1"
5Ir't5T9O.t4N8P8j165731 exit 1
6@`;lq*N4c"AE165731}
51Testing软件测试网;gI&rY\&Y}lastchar()
51Testing软件测试网$Hf`Q7N{
51Testing软件测试网5G%Q?7X+bD"x # return the last character of a string in $rval
51Testing软件测试网DlA4p+Z8a9HC s[2]D if [ -z "$1" ]; then
51Testing软件测试网 U/\g2V8J~5Aa E9C # empty string
51Testing软件测试网mM(n AZG rval=""
51Testing软件测试网V5S {y;S return
51Testing软件测试网 vk*WL ]S9f'r R fi
51Testing软件测试网2XYE@8{2Gx # wc puts some space behind the output this is why we need sed:
51Testing软件测试网;`GN(nr(ev numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
7_'U9O,B5Wh.t ]]'u165731 # now cut out the last char
51Testing软件测试网 Z&Y.R]p$s7P}%O;eI'C rval=`echo -n "$1" | cut -b $numofchar`
;S1r-f_/N;z&w165731}
51Testing软件测试网bZd4m9echop()
51Testing软件测试网*l2H$O-f }?_"Zr9N9Z`{
51Testing软件测试网p)R1Z%NLfY ac`U # remove the last character in string and return it in $rval
51Testing软件测试网FN+``ga0[FI if [ -z "$1" ]; then
51Testing软件测试网 |qInc.WP-KX # empty string
EL j+?d165731 rval=""
(kcCAJG%C_GF165731 return
51Testing软件测试网 M,SH8C'`8h| fi
n V/s l,o9O165731 # wc puts some space behind the output this is why we need sed:
51Testing软件测试网m'c&Di^ sZ2w1k_ numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
3Y r/t-E K VgO)f.h165731 if [ "$numofchar" = "1" ]; then
F/_!}u)hk#p165731 # only one char in string
@%Wd'p9g}165731 rval=""
51Testing软件测试网a+y La VP return
)^.I:_ Yo165731 fi
51Testing软件测试网g`MY)bV;{ numofcharminus1=`expr $numofchar "-" 1`
5q1s o7C0J O,?~ n165731 # now cut all but the last char:
51Testing软件测试网a kMxXu:g g$V!e!f rval=`echo -n "$1" | cut -b 0-${numofcharminus1}`
51Testing软件测试网?0y-W"zQMb}
d-nb{ ^6l5A165731while [ -n "$1" ]; do
6K/Kt g iQc zk*Q165731case $1 in
51Testing软件测试网9~:ZG|~;t*dJ -h) help;shift 1;; # function help is called
51Testing软件测试网z7^ d;y g-bz[xw --) shift;break;; # end of options
G'Q%IZ@)T&}Sb(m165731 -*) error "error: no such option $1. -h for help";;
NU`wUoT T,T165731 *) break;;
(Q7{nKzfmJ165731esac
51Testing软件测试网5aY%G%\s:tdone
51Testing软件测试网g9yua4`D.\# The main program
$ER*W|C ?'H*o&j165731sum=0
51Testing软件测试网'YL!k8["W(u~weight=1
51Testing软件测试网cl+V"w V*rk# one arg must be given:
51Testing软件测试网%k_7mEd.yv(m[ -z "$1" ] && help
51Testing软件测试网 T,|A,e5E%Vkq\[binnum="$1"
F)_e3_ J ?165731binnumorig="$1"
51Testing软件测试网ZmX8o|j Cwhile [ -n "$binnum" ]; do
51Testing软件测试网2K Hs/P0O9u+xK,F lastchar "$binnum"
/h VU:L{`K165731 if [ "$rval" = "1" ]; then
51Testing软件测试网L'BL9A,K;A?%R%W sum=`expr "$weight" "+" "$sum"`
_I _?4d~A165731 fi
51Testing软件测试网*h6aJE%}k # remove the last position in $binnum
w'~^ N*l4H165731 chop "$binnum"
51Testing软件测试网+lmr`Yj:w binnum="$rval"
1p&t0paQ165731 weight=`expr "$weight" "*" 2`
w-nGrm_l1J165731done
7{3_0c-m#C |.j0m165731echo "binary $binnumorig is decimal $sum"
d b N?gJs9c165731 该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..),比如二进制"10"可以这样转换成十进制:
51Testing软件测试网6SNN$@XjU#Ni0 * 1 + 1 * 2 = 2
s0b(A"P |'J9