| 论坛注册| 加入收藏 | 设为首页| RSS
Google
您当前的位置:首页 > Linux频道 > Linux开发区 > SHELL

linux shell 脚本入门

时间:2007-02-13 12:23:22  来源:Linux联盟收集整理  作者:
fCeLinux联盟

6. Here documents fCeLinux联盟

当要将几行文字传递给一个命令时,here documents(译者注:目前还没有见到过对该词适合的翻译)fCeLinux联盟
一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个 here documentsfCeLinux联盟
就不必用echo函数一行行输出。 一个 "Here document" 以 << 开头,后面接上一个字符串,这个字符串fCeLinux联盟
还必须出现在here document的末尾。下面是一个例子,在该例子中,我们对多个文件进行重命名,并且fCeLinux联盟
使用here documents打印帮助: fCeLinux联盟
#!/bin/sh fCeLinux联盟
# we have less than 3 arguments. Print the help text: fCeLinux联盟
if [ $# -lt 3 ] ; then fCeLinux联盟
cat < fCeLinux联盟
ren -- renames a number of files using sed regular expressions fCeLinux联盟
USAGE: ren 'regexp' 'replacement' files... fCeLinux联盟
EXAMPLE: rename all *.HTM files in *.html: fCeLinux联盟
ren 'HTM$' 'html' *.HTM fCeLinux联盟
HELP fCeLinux联盟
exit 0 fCeLinux联盟
fi fCeLinux联盟
OLD="$1" fCeLinux联盟
NEW="$2" fCeLinux联盟
# The shift command removes one argument from the list of fCeLinux联盟
# command line arguments. fCeLinux联盟
shift fCeLinux联盟
shift fCeLinux联盟
# $* contains now all the files: fCeLinux联盟
for file in $*; do fCeLinux联盟
if [ -f "$file" ] ; then fCeLinux联盟
newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"` fCeLinux联盟
if [ -f "$newfile" ]; then fCeLinux联盟
echo "ERROR: $newfile exists already" fCeLinux联盟
else fCeLinux联盟
echo "renaming $file to $newfile ..." fCeLinux联盟
mv "$file" "$newfile" fCeLinux联盟
fi fCeLinux联盟
fi fCeLinux联盟
done fCeLinux联盟
这是一个复杂一些的例子。让我们详细讨论一下。第一个if表达式判断输入命令行参数是fCeLinux联盟
否小于3个 (特殊变量$# 表示包含参数的个数) 。如果输入参数小于3个,则将帮助文字传递fCeLinux联盟
给cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出。 如果输入参数等fCeLinux联盟
于或大于3个,我们就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。下一步,我fCeLinux联盟
们使用shift命令将第一个和第二个参数从 参数列表中删除,这样原来的第三个参数就成为参fCeLinux联盟
数列表$*的第一个参数。然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。fCeLinux联盟
接着我 们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后fCeLinux联盟
将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目 的:得到了旧文件名和新fCeLinux联盟
文件名。然后使用mv命令进行重命名。 fCeLinux联盟
4)函数fCeLinux联盟
如果您写了一些稍微复杂一些的程序,您就会发现在程序中可能在几个地方使用了相同的代码,fCeLinux联盟
并且您也会发现,如果我们使用了函数,会方便很多。一个函数是这个样子的: fCeLinux联盟
functionname() fCeLinux联盟
{ fCeLinux联盟
# inside the body $1 is the first argument given to the function fCeLinux联盟
# $2 the second ... fCeLinux联盟
body fCeLinux联盟
} fCeLinux联盟
您需要在每个程序的开始对函数进行声明。
fCeLinux联盟

下面是一个叫做xtitlebar的脚本,使用这个脚本您可以改变终端窗口的名称。fCeLinux联盟
这里使用了一个叫做help的函数。正如您可以看到的那样,这个定义的函数被使用了两次。 fCeLinux联盟
#!/bin/sh fCeLinux联盟
# vim: set sw=4 ts=4 et: fCeLinux联盟
help() fCeLinux联盟
{ fCeLinux联盟
cat < fCeLinux联盟
xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole fCeLinux联盟
USAGE: xtitlebar [-h] "string_for_titelbar" fCeLinux联盟
OPTIONS: -h help text fCeLinux联盟
EXAMPLE: xtitlebar "cvs" fCeLinux联盟
HELP fCeLinux联盟
exit 0 fCeLinux联盟
} fCeLinux联盟
# in case of error or if -h is given we call the function help: fCeLinux联盟
[ -z "$1" ] && help fCeLinux联盟
[ "$1" = "-h" ] && help fCeLinux联盟
# send the escape sequence to change the xterm titelbar: fCeLinux联盟
echo -e "33]0;$107" fCeLinux联盟
#
fCeLinux联盟

在脚本中提供帮助是一种很好的编程习惯,这样方便其他用户(和您)使用和理解脚本。 fCeLinux联盟
命令行参数 fCeLinux联盟
我们已经见过$* 和 $1, $2 ... $9 等特殊变量,这些特殊变量包含了用户从命令fCeLinux联盟
行输入的参数。迄今为止,我们仅仅了解了一些简单的命令行语法(比如一些强制性的fCeLinux联盟
参数和查看帮助的-h选项)。 但是在编写更复杂的程序时,您可能会发现您需要更多的fCeLinux联盟
自定义的选项。通常的惯例是在所有可选的参数之前加一个减号,后面再加上参数值 (fCeLinux联盟
比如文件名)。 fCeLinux联盟
有好多方法可以实现对输入参数的分析,但是下面的使用case表达式的例子无遗是一个不错的方法。 fCeLinux联盟
#!/bin/sh fCeLinux联盟
help() fCeLinux联盟
{ fCeLinux联盟
cat < fCeLinux联盟
This is a generic command line parser demo. fCeLinux联盟
USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2 fCeLinux联盟
HELP fCeLinux联盟
exit 0 fCeLinux联盟
} fCeLinux联盟
while [ -n "$1" ]; do fCeLinux联盟
case $1 in fCeLinux联盟
-h) help;shift 1;; # function help is called fCeLinux联盟
-f) opt_f=1;shift 1;; # variable opt_f is set fCeLinux联盟
-l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2 fCeLinux联盟
--) shift;break;; # end of options fCeLinux联盟
-*) echo "error: no such option $1. -h for help";exit 1;; fCeLinux联盟
*) break;; fCeLinux联盟
esac fCeLinux联盟
done
fCeLinux联盟

echo "opt_f is $opt_f" fCeLinux联盟
echo "opt_l is $opt_l" fCeLinux联盟
echo "first arg is $1" fCeLinux联盟
echo "2nd arg is $2" fCeLinux联盟
您可以这样运行该脚本: fCeLinux联盟
cmdparser -l hello -f -- -somefile1 somefile2 fCeLinux联盟
返回的结果是: fCeLinux联盟
opt_f is 1 fCeLinux联盟
opt_l is hello fCeLinux联盟
first arg is -somefile1 fCeLinux联盟
2nd arg is somefile2 fCeLinux联盟
这个脚本是如何工作的呢?脚本首先在所有输入命令行参数中进行循环,将输入参数fCeLinux联盟
与case表达式进行比较,如果匹配则设置一个变量并且移除该参数。根据unix系统的惯例,fCeLinux联盟
首先输入的应该是包含减号的参数.fCeLinux联盟
第2部分 实例fCeLinux联盟
现在我们来讨论编写一个脚本的一般步骤。任何优秀的脚本都应该具有帮助和输入参数。并且写一个伪脚本(framework.sh),该脚本包含了大多数脚本都需要的框架结构,是一个非常不错的主意。这时候,在写一个新的脚本时我们只需要执行一下copy命令: fCeLinux联盟
cp framework.sh myscript fCeLinux联盟
然后再插入自己的函数。 fCeLinux联盟
让我们再看两个例子: fCeLinux联盟
二进制到十进制的转换 fCeLinux联盟
脚本 b2d 将二进制数 (比如 1101) 转换为相应的十进制数。这也是一个用expr命令进行数学运算的例子: fCeLinux联盟
#!/bin/sh fCeLinux联盟
# vim: set sw=4 ts=4 et: fCeLinux联盟
help() fCeLinux联盟
{ fCeLinux联盟
cat < fCeLinux联盟
b2h -- convert binary to decimal fCeLinux联盟
USAGE: b2h [-h] binarynum fCeLinux联盟
OPTIONS: -h help text fCeLinux联盟
EXAMPLE: b2h 111010 fCeLinux联盟
will return 58 fCeLinux联盟
HELP fCeLinux联盟
exit 0 fCeLinux联盟
} fCeLinux联盟
error() fCeLinux联盟
{ fCeLinux联盟
# print an error and exit fCeLinux联盟
echo "$1" fCeLinux联盟
exit 1 fCeLinux联盟
} fCeLinux联盟
lastchar() fCeLinux联盟
{ fCeLinux联盟
# return the last character of a string in $rval fCeLinux联盟
if [ -z "$1" ]; then fCeLinux联盟
# empty string fCeLinux联盟
rval="" fCeLinux联盟
return fCeLinux联盟
fi fCeLinux联盟
# wc puts some space behind the output this is why we need sed: fCeLinux联盟
numofchar=`echo -n "$1" | wc -c | sed 's/ //g' ` fCeLinux联盟
# now cut out the last char fCeLinux联盟
rval=`echo -n "$1" | cut -b $numofchar` fCeLinux联盟
}
fCeLinux联盟

chop() fCeLinux联盟
{ fCeLinux联盟
# remove the last character in string and return it in $rval fCeLinux联盟
if [ -z "$1" ]; then fCeLinux联盟
# empty string fCeLinux联盟
rval="" fCeLinux联盟
return fCeLinux联盟
fi fCeLinux联盟
# wc puts some space behind the output this is why we need sed: fCeLinux联盟
numofchar=`echo -n "$1" | wc -c | sed 's/ //g' ` fCeLinux联盟
if [ "$numofchar" = "1" ]; then fCeLinux联盟
# only one char in string fCeLinux联盟
rval="" fCeLinux联盟
return fCeLinux联盟
fi fCeLinux联盟
numofcharminus1=`expr $numofchar "-" 1` fCeLinux联盟
# now cut all but the last char: fCeLinux联盟
rval=`echo -n "$1" | cut -b 0-${numofcharminus1}` fCeLinux联盟
} fCeLinux联盟
while [ -n "$1" ]; do fCeLinux联盟
case $1 in fCeLinux联盟
-h) help;shift 1;; # function help is called fCeLinux联盟
--) shift;break;; # end of options fCeLinux联盟
-*) error "error: no such option $1. -h for help";; fCeLinux联盟
*) break;; fCeLinux联盟
esac fCeLinux联盟
done fCeLinux联盟
# The main program fCeLinux联盟
sum=0 fCeLinux联盟
weight=1 fCeLinux联盟
# one arg must be given: fCeLinux联盟
[ -z "$1" ] && help fCeLinux联盟
binnum="$1" fCeLinux联盟
binnumorig="$1"
fCeLinux联盟

while [ -n "$binnum" ]; do fCeLinux联盟
lastchar "$binnum" fCeLinux联盟
if [ "$rval" = "1" ]; then fCeLinux联盟
sum=`expr "$weight" "+" "$sum"` fCeLinux联盟
fi fCeLinux联盟
# remove the last position in $binnum fCeLinux联盟
chop "$binnum" fCeLinux联盟
binnum="$rval" fCeLinux联盟
weight=`expr "$weight" "*" 2` fCeLinux联盟
done fCeLinux联盟
echo "binary $binnumorig is decimal $sum" fCeLinux联盟
该脚本使用的算法是利用十进制和二进制数权值 (1,2,4,8,16,..),比如二进制"10"可fCeLinux联盟
以这样转换成十进制: fCeLinux联盟
0 * 1 + 1 * 2 = 2 fCeLinux联盟
为了得到单个的二进制数我们是用了lastchar 函数。该函数使用wc –c计算字符个数,fCeLinux联盟
然后使用cut命令取出末尾一个字符。Chop函数的功能则是移除最后一个字符。
fCeLinux联盟

文件循环程序 fCeLinux联盟
或许您是想将所有发出的邮件保存到一个文件中的人们中的一员,但是在过了几个月fCeLinux联盟
以后,这个文件可能会变得很大以至于使对该文件的访问速度变慢。下面的 脚本rotatefilefCeLinux联盟
可以解决这个问题。这个脚本可以重命名邮件保存文件(假设为outmail)为outmail.1,fCeLinux联盟
而对于outmail.1就变成了outmail.2 等等等等... fCeLinux联盟
#!/bin/sh fCeLinux联盟
# vim: set sw=4 ts=4 et: fCeLinux联盟
ver="0.1" fCeLinux联盟
help() fCeLinux联盟
{ fCeLinux联盟
cat < fCeLinux联盟
rotatefile -- rotate the file name
fCeLinux联盟

USAGE: rotatefile [-h] filename fCeLinux联盟

OPTIONS: -h help text fCeLinux联盟
EXAMPLE: rotatefile out fCeLinux联盟
This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1 fCeLinux联盟
and create an empty out-file fCeLinux联盟
The max number is 10 fCeLinux联盟
version $ver fCeLinux联盟
HELP fCeLinux联盟
exit 0 fCeLinux联盟
}
fCeLinux联盟

error() fCeLinux联盟
{ fCeLinux联盟
echo "$1" fCeLinux联盟
exit 1 fCeLinux联盟
} fCeLinux联盟
while [ -n "$1" ]; do fCeLinux联盟
case $1 in fCeLinux联盟
-h) help;shift 1;; fCeLinux联盟
--) break;; fCeLinux联盟
-*) echo "error: no such option $1. -h for help";exit 1;; fCeLinux联盟
*) break;; fCeLinux联盟
esac fCeLinux联盟
done fCeLinux联盟
# input check: fCeLinux联盟
if [ -z "$1" ] ; then fCeLinux联盟
error "ERROR: you must specify a file, use -h for help" fCeLinux联盟
fi fCeLinux联盟
filen="$1" fCeLinux联盟
# rename any .1 , .2 etc file: fCeLinux联盟
for n in 9 8 7 6 5 4 3 2 1; do fCeLinux联盟
if [ -f "$filen.$n" ]; then fCeLinux联盟
p=`expr $n + 1` fCeLinux联盟
echo "mv $filen.$n $filen.$p" fCeLinux联盟
mv $filen.$n $filen.$p fCeLinux联盟
fi fCeLinux联盟
done fCeLinux联盟
# rename the original file: fCeLinux联盟
if [ -f "$filen" ]; then fCeLinux联盟
echo "mv $filen $filen.1" fCeLinux联盟
mv $filen $filen.1 fCeLinux联盟
fi fCeLinux联盟
echo touch $filen fCeLinux联盟
touch $filen fCeLinux联盟
这个脚本是如何工作的呢?在检测用户提供了一个文件名以后,我们进行一个9到1的循环。文件9被命名为10,文件8重命名为9等等。循环完成之后,我们将原始文件命名为文件1同时建立一个与原始文件同名的空文件。 fCeLinux联盟
调试 //--------------------------------------- fCeLinux联盟
//--------------------------------------- fCeLinux联盟
最简单的调试命令当然是使用echo命令。您可以使用echo在任何怀疑出错的地方打印任何变量值。这也是绝大多数的shell程序员要花费80%的时间来调试程序的原因。Shell程序的好处在于不需要重新编译,插入一个echo命令也不需要多少时间。 fCeLinux联盟
shell也有一个真实的调试模式。如果在脚本"strangescript" 中有错误,您可以这样来进行调试: fCeLinux联盟
sh -x strangescript fCeLinux联盟
这将执行该脚本并显示所有变量的值。 fCeLinux联盟
shell还有一个不需要执行脚本只是检查语法的模式。可以这样使用: fCeLinux联盟
sh -n your_script fCeLinux联盟
这将返回所有语法错误。 
fCeLinux联盟

来顶一下
近回首页
返回首页
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
相关文章
栏目更新
栏目热门