2.case
case :表达式可以用来匹配一个给定的字符Ԍ而不是数字?
case ... in
...) do something here ;;
esac
让我们看一个例子?file命o(h)可以辨别Z个给定文件的文gcdQ比如:(x)
file lf.gz
q将q回Q?
lf.gz: gzip compressed data, deflated, original filename,
last modified: Mon Aug 27 23:09:18 2001, os: Unix
我们利用q一点写了一个叫做smartzip的脚本,该脚本可以自动解压bzip2, gzip 和zip cd的压~文Ӟ(x)
#!/bin/sh
ftype=`file "Q?"`
case "Qftype" in
"Q?: Zip archive"*)
unzip "Q?" ;;
"Q?: gzip compressed"*)
gunzip "Q?" ;;
"Q?: bzip2 compressed"*)
bunzip2 "Q?" ;;
*) echo "File Q? can not be uncompressed with smartzip";;
esac
(zhn)可能注意到我们在这里用了一个特D的变量Q?。该变量包含了传递给该程序的W一个参数倹{?br />
也就是说Q当我们q行Q?
smartzip articles.zip
Q? 是字符?articles.zip
3. selsect
select 表达式是一Ubash的扩展应用,其擅长于交互式使用。用户可以从一l不同的gq行选择?
select var in ... ; do
break
done
.... now Q?var can be used ....
下面是一个例子:(x)
#!/bin/sh
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
break
done
echo "You have selected Qvar"
下面是该脚本q行的结果:(x)
What is your favourite OS?
1) Linux
2) Gnu Hurd
3) Free BSD
4) Other
#? 1
You have selected Linux
注:(x)var是个变量Q可以换成其它的倹{break用来跛_循环Q如果没有break则一直@环下厅Rdone与select对应?br />
4.loop
loop表达式:(x)
while ...; do
....
done
while-loop 运行直到表辑ּ试为真。will run while the expression that we test for is true.
关键?break" 用来跛_循环。而关键字”continue”用来不执行余下的部分而直接蟩C一个@环?
for-loop表达式查看一个字W串列表 (字符串用I格分隔) 然后其赋给一个变量:(x)
1, for var in ....; do
....
done
在下面的例子中,分别打印ABC到屏q上Q?
#!/bin/sh
for var in A B C ; do
echo "var is Qvar"
done
2, for (( 条g一; 条g? 条g?);do
...
done
例:(x)
for ((i=1;i<10;i=Q[Qi+1]));do
echo "a"
done
输出Q?/p>
a
a
a
a
a
a
a
a
a
条g一Q这可以看成是『初始倹{,如上面的例子中,初始值是 i=1 啦!
条g二:(x)q可以看成是『符合倹{,如上面的例子中,?i<=100 的时候都是符合条件的Q?
条g三:(x)q可以看成是『步阶』!也就是说Q?i 每次都加一Q?所以啦Q上面的例子是说Q由 i=1 开始到 i<= 100 Q每?i 都加一来执行底下的E序D(是 s=s+i Q,?i >100 Q也是 i=101 Q就跛_q一D늨序段Q怎样Q不隑Q?/p>
下面是一个更为有用的脚本showrpmQ其功能是打C些RPM包的l计信息Q?
#!/bin/sh
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in Q?; do
if [ -r "Qrpmpackage" ];then
echo "=============== Qrpmpackage =============="
rpm -qi -p Qrpmpackage
else
echo "ERROR: cannot read file Qrpmpackage"
fi
done
q里出现了第二个Ҏ(gu)的变量$*Q该变量包含了所有输入的命o(h)行参数倹{?br />
如果(zhn)运行showrpm openssh.rpm w3m.rpm webgrep.rpm
此时 Q? 包含?3 个字W串Q即openssh.rpm, w3m.rpm and webgrep.rpm.
『untilQ直到条件相同的时候才dE序』;
『whileQ当条g相同的时候,ql做Q?nbsp;
until [ condition1 ] && { || } [ condition2 ] ...
5. 引号
在向E序传递Q何参C前,E序?x)扩展通配W和变量。这里所谓扩展的意思是E序?x)把通配W(比如*Q替换成合适的文g名,它变量替换成变量倹{ؓ(f)了防 止程序作q种替换Q?zhn)可以使用引号Q让我们来看一个例子,假设在当前目录下有一些文Ӟ两个jpg文gQ?mail.jpg 和tux.jpg?
~译SHELL脚本
#ch#!/bin/sh mod +x filename
cho *.jpg ./filename 来执行?zhn)的脚本?
q将打印?mail.jpg tux.jpg"的结果?
引号 (单引号和双引? 防止这U通配W扩展:(x)
#!/bin/sh
echo "*.jpg"
echo '*.jpg'
q将打印"*.jpg" 两次?
单引h严格一些。它可以防止M变量扩展。双引号可以防止通配W扩展但允许变量扩展?
#!/bin/sh
echo QSHELL
echo "QSHELL"
echo 'QSHELL'
q行l果为:(x)
/bin/bash
/bin/bash
QSHELL
最后,q有一U防止这U扩展的Ҏ(gu)Q那是使用转义字符——反斜杆Q?
echo *.jpg
echo QSHELL
q将输出Q?
*.jpg
QSHELL
6. Here documents
当要几行文字传递给一个命令时Qhere documentsQ译者注Q目前还没有见到q对该词适合的翻译)一U不错的Ҏ(gu)。对每个脚本写一D帮助性的文字是很有用的,此时如果我们四有那个 here documents׃必用echo函数一行行输出?一?"Here document" ?<< 开_(d)后面接上一个字W串Q这个字W串q必d现在here document的末。下面是一个例子,在该例子中,我们对多个文件进行重命名Qƈ且用here documents打印帮助Q?
#!/bin/sh
# we have less than 3 arguments. Print the help text:
if [ Q? -lt 3 ] ; then
cat <
ren -- renames a number of files using sed regular expressions
USAGE: ren 'regexp' 'replacement' files...
EXAMPLE: rename all *.HTM files in *.html:
ren 'HTMQ? 'html' *.HTM
HELP
exit 0
fi
OLD="Q?"
NEW="Q?"
# The shift command removes one argument from the list of
# command line arguments.
shift
shift
# Q? contains now all the files:
for file in Q?; do
if [ -f "Qfile" ] ; then
newfile=`echo "Qfile" | sed "s/Q{OLD}/Q{NEW}/g"`
if [ -f "Qnewfile" ]; then
echo "ERROR: Qnewfile exists already"
else
echo "renaming Qfile to Qnewfile ..."
mv "Qfile" "Qnewfile"
fi
fi
done
q是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是否于3?(Ҏ(gu)变量Q? 表示包含参数的个? 。如果输入参数小?个,则将帮助文字传递给cat命o(h)Q然后由cat命o(h)其打印在屏q上。打印帮助文字后E序退出?如果输入参数{于或大?个,我们将W一个参数赋值给变量OLDQ第二个参数赋值给变量NEW。下一步,我们使用shift命o(h)第一个和W二个参C 参数列表中删除,q样原来的第三个参数成为参数列表$*的第一个参数。然后我们开始@环,命o(h)行参数列表被一个接一个地被赋值给变量Qfile。接着?们判断该文g是否存在Q如果存在则通过sed命o(h)搜烦和替换来产生新的文g名。然后将反短斜线内命令结果赋值给newfile。这h们就辑ֈ了我们的?的:(x)得到了旧文g名和?br />
文g名。然后用mv命o(h)q行重命名?
4)函数
如果(zhn)写了一些稍微复杂一些的E序Q?zhn)׃?x)发现在程序中可能在几个地方用了相同的代码,q且(zhn)也?x)发玎ͼ如果我们使用了函敎ͼ会(x)方便很多。一个函数是q个样子的:(x)
functionname()
{
# inside the body Q? is the first argument given to the function
# Q? the second ...
body
}
(zhn)需要在每个E序的开始对函数q行声明?
下面是一个叫做xtitlebar的脚本,使用q个脚本(zhn)可以改变终端窗口的名称?br />
q里使用了一个叫做help的函数。正如?zhn)可以看到的那Pq个定义的函数被使用了两ơ?
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
cat <
xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole
USAGE: xtitlebar [-h] "string_for_titelbar"
OPTIONS: -h help text
EXAMPLE: xtitlebar "cvs"
HELP
exit 0
}
# in case of error or if -h is given we call the function help:
[ -z "Q?" ] && help
[ "Q?" = "-h" ] && help
# send the escape sequence to change the xterm titelbar:
echo -e "33]0;Q?07"
#
在脚本中提供帮助是一U很好的~程?fn)惯Q这h便其他用P和?zhn)Q用和理解脚本?
命o(h)行参?
我们已经见过Q? ?Q?, Q? ... Q? {特D变量,q些Ҏ(gu)变量包含了用户从命o(h)行输入的参数。迄今ؓ(f)止,我们仅仅了解了一些简单的命o(h)行语法(比如一些强制性的参数和查看帮助的-h选项Q?但是在编写更复杂的程序时Q?zhn)可能会(x)发现(zhn)需要更多的自定义的选项。通常的惯例是在所有可选的参数之前加一个减P后面再加上参数?(比如文g?。有好多Ҏ(gu)可以实现对输入参数的分析Q但是下面的使用case表达式的例子无遗是一个不错的Ҏ(gu)?
#!/bin/sh
help()
{
cat <
This is a generic command line parser demo.
USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2
HELP
exit 0
}
while [ -n "Q?" ]; do
case Q? in
-h) help;shift 1;; # function help is called
-f) opt_f=1;shift 1;; # variable opt_f is set
-l) opt_l=Q?;shift 2;; # -l takes an argument -> shift by 2
--) shift;break;; # end of options
-*) echo "error: no such option Q?. -h for help";exit 1;;
*) break;;
esac
done
echo "opt_f is Qopt_f"
echo "opt_l is Qopt_l"
echo "first arg is Q?"
echo "2nd arg is Q?"
(zhn)可以这栯行该脚本Q?
cmdparser -l hello -f -- -somefile1 somefile2
q回的结果是Q?
opt_f is 1
opt_l is hello
first arg is -somefile1
2nd arg is somefile2
q个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行@环,输入参Ccase表达式进行比较,如果匚w则设|一个变量ƈ且移除该参数。根据unixpȝ的惯例,首先输入的应该是包含减号的参?
W?部分 实例
现在我们来讨论编写一个脚本的一般步骤。Q何优U的脚本都应该h帮助和输入参数。ƈ且写一个伪脚本Qframework.shQ,该脚本包含了大多数脚本都需要的框架l构Q是一个非怸错的L。这时候,在写一个新的脚本时我们只需要执行一下copy命o(h)Q?
cp framework.sh myscript
然后再插入自q函数?
让我们再看两个例子:(x)
二进制到十进制的转换
脚本 b2d 二q制?(比如 1101) 转换为相应的十进制数。这也是一个用expr命o(h)q行数学q算的例子:(x)
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
cat <
b2h -- convert binary to decimal
USAGE: b2h [-h] binarynum
OPTIONS: -h help text
EXAMPLE: b2h 111010
will return 58
HELP
exit 0
}
error()
{
# print an error and exit
echo "Q?"
exit 1
}
lastchar()
{
# return the last character of a string in Qrval
if [ -z "Q?" ]; then
# empty string
rval=""
return
fi
# wc puts some space behind the output this is why we need sed:
numofchar=`echo -n "Q?" | wc -c | sed 's/ //g' `
# now cut out the last char
rval=`echo -n "Q?" | cut -b Qnumofchar`
}
chop()
{
# remove the last character in string and return it in Qrval
if [ -z "Q?" ]; then
# empty string
rval=""
return
fi
# wc puts some space behind the output this is why we need sed:
numofchar=`echo -n "Q?" | wc -c | sed 's/ //g' `
if [ "Qnumofchar" = "1" ]; then
# only one char in string
rval=""
return
fi
numofcharminus1=`expr Qnumofchar "-" 1`
# now cut all but the last char:
rval=`echo -n "Q?" | cut -b 0-Q{numofcharminus1}`
}
while [ -n "Q?" ]; do
case Q? in
-h) help;shift 1;; # function help is called
--) shift;break;; # end of options
-*) error "error: no such option Q?. -h for help";;
*) break;;
esac
done
# The main program
sum=0
weight=1
# one arg must be given:
[ -z "Q?" ] && help
binnum="Q?"
binnumorig="Q?"
while [ -n "Qbinnum" ]; do
lastchar "Qbinnum"
if [ "Qrval" = "1" ]; then
sum=`expr "Qweight" "+" "Qsum"`
fi
# remove the last position in Qbinnum
chop "Qbinnum"
binnum="Qrval"
weight=`expr "Qweight" "*" 2`
done
echo "binary Qbinnumorig is decimal Qsum"
该脚本用的法是利用十q制和二q制数权?(1,2,4,8,16,..)Q比如二q制"10"可以q样转换成十q制Q?
0 * 1 + 1 * 2 = 2
Z得到单个的二q制数我们是用了lastchar 函数。该函数使用wc –c计算字符个数Q然后用cut命o(h)取出末尾一个字W。Chop函数的功能则是移除最后一个字W?
文g循环E序
或许(zhn)是惛_所有发出的邮g保存C个文件中的h们中的一员,但是在过了几个月以后Q这个文件可能会(x)变得很大以至于对该文g的访问速度变慢。下面的 脚本rotatefile可以解决q个问题。这个脚本可以重命名邮g保存文gQ假设ؓ(f)outmailQؓ(f)outmail.1Q而对于outmail.1变成了outmail.2 {等{等...
#!/bin/sh
# vim: set sw=4 ts=4 et:
ver="0.1"
help()
{
cat <
rotatefile -- rotate the file name
USAGE: rotatefile [-h] filename
OPTIONS: -h help text
EXAMPLE: rotatefile out
This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1
and create an empty out-file
The max number is 10
version Qver
HELP
exit 0
}
error()
{
echo "Q?"
exit 1
}
while [ -n "Q?" ]; do
case Q? in
-h) help;shift 1;;
--) break;;
-*) echo "error: no such option Q?. -h for help";exit 1;;
*) break;;
esac
done
# input check:
if [ -z "Q?" ] ; then
error "ERROR: you must specify a file, use -h for help"
fi
filen="Q?"
# rename any .1 , .2 etc file:
for n in 9 8 7 6 5 4 3 2 1; do
if [ -f "Qfilen.Qn" ]; then
p=`expr Qn + 1`
echo "mv Qfilen.Qn Qfilen.Qp"
mv Qfilen.Qn Qfilen.Qp
fi
done
# rename the original file:
if [ -f "Qfilen" ]; then
echo "mv Qfilen Qfilen.1"
mv Qfilen Qfilen.1
fi
echo touch Qfilen
touch Qfilen
q个脚本是如何工作的呢?在检用h供了一个文件名以后Q我们进行一??的@环。文?被命名ؓ(f)10Q文?重命名ؓ(f)9{等。@环完成之后,我们原始文件命名ؓ(f)文g1同时建立一个与原始文g同名的空文g?
调试
最单的调试命o(h)当然是用echo命o(h)。?zhn)可以使用echo在Q何怀疑出错的地方打印M变量倹{这也是l大多数的shellE序员要p80%的时间来调试E序的原因。ShellE序的好处在于不需要重新编译,插入一个echo命o(h)也不需要多时间?
shell也有一个真实的调试模式。如果在脚本"strangescript" 中有错误Q?zhn)可以q样来进行调试:(x)
sh -x strangescript
q将执行该脚本ƈ昄所有变量的倹{?
shellq有一个不需要执行脚本只是检查语法的模式。可以这样用:(x)
sh -n your_script
q将q回所有语法错误?/p>