bash编程实例文档格式.docx
《bash编程实例文档格式.docx》由会员分享,可在线阅读,更多相关《bash编程实例文档格式.docx(25页珍藏版)》请在冰点文库上搜索。
![bash编程实例文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-5/5/857e1b59-5d48-4cc4-88b0-6c8bf5808935/857e1b59-5d48-4cc4-88b0-6c8bf58089351.gif)
$myvar='
Thisismyenvironmentvariable!
'
以上命令定义了一个名为"
myvar"
的环境变量,并包含字符串"
"
。
以上有几点注意事项:
第一,在等号"
="
的两边没有空格,任何空格将导致错误(试一下看看)。
第二个件要注意的事是:
虽然在定义一个字时可以省略引号,但是当定义的环境变量值多于一个字时(包含空格或制表键),引号是必须的。
第三,虽然通常可以用双引号来替代单引号,但在上例中,这样做会导致错误。
为什么呢?
因为使用单引号禁用了称为扩展的bash特性,其中,特殊字符和字符系列由值替换。
例如,"
!
字符是历史扩展字符,bash通常将其替换为前面输入的命令。
(本系列文章中将不讲述历史扩展,因为它在bash编程中不常用。
有关历史扩展的详细信息,请参阅bash帮助页中的“历史扩展”一节。
)尽管这个类似于宏的功能很便利,但我们现在只想在环境变量后面加上一个简单的感叹号,而不是宏。
现在,让我们看一下如何实际使用环境变量。
这有一个例子:
$echo$myvar
Thisismyenvironmentvariable!
通过在环境变量的前面加上一个$,可以使bash用myvar的值替换它。
这在bash术语中叫做“变量扩展”。
但是,这样做将怎样:
$echofoo$myvarbar
foo
我们希望回显"
fooThisismyenvironmentvariable!
bar"
,但却不是这样。
错在哪里?
简单地说,bash变量扩展设施陷入了困惑。
它无法识别要扩展哪一个变量:
$m、$my、$myvar、$myvarbar等等。
如何更明确清楚地告述bash引用哪一个变量?
试一下这个:
$echofoo${myvar}bar
fooThisismyenvironmentvariable!
bar
如您所见,当环境变量没有与周围文本明显分开时,可以用花括号将它括起。
虽然$myvar可以更快输入,并且在大多数情况下正确工作,但${myvar}却能在几乎所有情况下正确通过语法分析。
除此之外,二者相同,将在本系列的余下部分看到变量扩展的两种形式。
请记住:
当环境变量没有用空白(空格或制表键)与周围文本分开时,请使用更明确的花括号形式。
回想一下,我们还提到过可以“导出”变量。
当导出环境变量时,它可以自动地由以后运行的任何脚本或可执行程序环境使用。
shell脚本可以使用shell的内置环境变量支持“到达”环境变量,而C程序可以使用getenv()函数调用。
这里有一些C代码示例,输入并编译它们--它将帮助我们从C的角度理解环境变量:
myvar.c--样本环境变量C程序
#include
intmain(void){
char*myenvvar=getenv("
EDITOR"
);
printf("
Theeditorenvironmentvariableissetto%s\n"
myenvvar);
}
将上面的代码保存到文件myenv.c中,然后发出以下命令进行编译:
$gccmyenv.c-omyenv
现在,目录中将有一个可执行程序,它在运行时将打印EDITOR环境变量的值(如果有值的话)。
这是在我机器上运行时的情况:
$./myenv
Theeditorenvironmentvariableissetto(null)
啊...因为没有将EDITOR环境变量设置成任何值,所以C程序得到一个空字符串。
让我们试着将它设置成特定值:
$EDITOR=xemacs
虽然希望myenv打印值"
xemacs"
,但是因为还没有导出环境变量,所以它却没有很好地工作。
这次让它正确工作:
$exportEDITOR
Theeditorenvironmentvariableissettoxemacs
现在,如您亲眼所见:
不导出环境变量,另一个进程(在本例中是示例C程序)就看不到环境变量。
顺便提一句,如果愿意,可以在一行定义并导出环境变量,如下所示:
$exportEDITOR=xemacs
这与两行版本的效果相同。
现在该演示如何使用unset来除去环境变量:
$unsetEDITOR
截断字符串概述
截断字符串是将初始字符串截断成较小的独立块,它是一般shell脚本每天执行的任务之一。
很多时候,shell脚本需要采用全限定路径,并找到结束的文件或目录。
虽然可以用bash编码实现(而且有趣),但标准basenameUNIX可执行程序可以极好地完成此工作:
$basename/usr/local/share/doc/foo/foo.txt
foo.txt
$basename/usr/home/drobbins
drobbins
Basename是一个截断字符串的极简便工具。
它的相关命令dirname返回basename丢弃的“另”一部分路径。
$dirname/usr/local/share/doc/foo/foo.txt
/usr/local/share/doc/foo
$dirname/usr/home/drobbins/
/usr/home
命令替换
需要知道一个简便操作:
如何创建一个包含可执行命令结果的环境变量。
这很容易:
$MYDIR=`dirname/usr/local/share/doc/foo/foo.txt`
$echo$MYDIR
上面所做的称为“命令替换”。
此例中有几点需要指出。
在第一行,简单地将要执行的命令以反引号括起。
那不是标准的单引号,而是键盘中通常位于Tab键之上的单引号。
可以用bash备用命令替换语法来做同样的事:
$MYDIR=$(dirname/usr/local/share/doc/foo/foo.txt)
如您所见,bash提供多种方法来执行完全一样的操作。
使用命令替换可以将任何命令或命令管道放在``或$()之间,并将其分配给环境变量。
真方便!
下面是一个例子,演示如何在命令替换中使用管道:
MYFILES=$(ls/etc|greppa)
bash-2.03$echo$MYFILES
pam.dpasswd
象专业人员那样截断字符串
尽管basename和dirname是很好的工具,但有时可能需要执行更高级的字符串“截断”,而不只是标准的路径名操作。
当需要更强的说服力时,可以利用bash内置的变量扩展功能。
已经使用了类似于${MYVAR}的标准类型的变量扩展。
但是bash自身也可以执行一些便利的字符串截断。
看一下这些例子:
$MYVAR=foodforthought.jpg
$echo${MYVAR##*fo}
rthought.jpg
$echo${MYVAR#*fo}
odforthought.jpg
在第一个例子中,输入了${MYVAR##*fo}。
它的确切含义是什么?
基本上,在${}中输入环境变量名称,两个##,然后是通配符("
*fo"
)。
然后,bash取得MYVAR,找到从字符串"
foodforthought.jpg"
开始处开始、且匹配通配符"
的最长子字符串,然后将其从字符串的开始处截去。
刚开始理解时会有些困难,为了感受一下这个特殊的"
##"
选项如何工作,让我们一步步地看看bash如何完成这个扩展。
首先,它从"
的开始处搜索与"
通配符匹配的子字符串。
以下是检查到的子字符串:
f
foMATCHES*fo
food
foodf
foodfoMATCHES*fo
foodfor
foodfort
foodforth
foodfortho
foodforthou
foodforthoug
foodforthought
foodforthought.j
foodforthought.jp
foodforthought.jpg
在搜索了匹配的字符串之后,可以看到bash找到两个匹配。
它选择最长的匹配,从初始字符串的开始处除去,然后返回结果。
上面所示的第二个变量扩展形式看起来与第一个相同,但是它只使用一个"
#"
--并且bash执行几乎同样的过程。
它查看与第一个例子相同的子字符串系列,但是bash从初始字符串除去最短的匹配,然后返回结果。
所以,一查到"
fo"
子字符串,它就从字符串中除去"
,然后返回"
odforthought.jpg"
这样说可能会令人十分困惑,下面以一简单方式记住这个功能。
当搜索最长匹配时,使用##(因为##比#长)。
当搜索最短匹配时,使用#。
看,不难记吧!
等一下,怎样记住应该使用'
#'
字符来从字符串开始部分除去?
很简单!
注意到了吗:
在美国键盘上,shift-4是"
$"
,它是bash变量扩展字符。
在键盘上,紧靠"
左边的是"
这样,可以看到:
位于"
的“开始处”,因此(根据我们的记忆法),"
从字符串的开始处除去字符。
您可能要问:
如何从字符串末尾除去字符。
如果猜到我们使用美国键盘上紧靠"
右边的字符("
%),那就猜对了。
这里有一些简单的例子,解释如何截去字符串的末尾部分:
$MYFOO="
chickensoup.tar.gz"
$echo${MYFOO%%.*}
chickensoup
$echo${MYFOO%.*}
chickensoup.tar
正如您所见,除了将匹配通配符从字符串末尾除去之外,%和%%变量扩展选项与#和##的工作方式相同。
请注意:
如果要从末尾除去特定子字符串,不必使用"
*"
字符:
MYFOOD="
chickensoup"
$echo${MYFOOD%%soup}
chicken
在此例中,使用"
%%"
或"
%"
并不重要,因为只能有一个匹配。
还要记住:
如果忘记了应该使用"
还是"
,则看一下键盘上的3、4和5键,然后猜出来。
可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。
试着在bash中输入以下行:
$EXCLAIM=cowabunga
$echo${EXCLAIM:
0:
3}
cow
3:
7}
abunga
这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。
应用字符串截断
现在我们已经学习了所有截断字符串的知识,下面写一个简单短小的shell脚本。
我们的脚本将接受一个文件作为自变量,然后打印:
该文件是否是一个tar文件。
要确定它是否是tar文件,将在文件末尾查找模式"
.tar"
如下所示:
mytar.sh--一个简单的脚本
#!
/bin/bash
if["
${1##*.}"
="
tar"
]
then
echoThisappearstobeatarball.
else
echoAtfirstglance,thisdoesnotappeartobeatarball.
fi
要运行此脚本,将它输入到文件mytar.sh中,然后输入"
chmod755mytar.sh"
,生成可执行文件。
然后,如下做一下tar文件试验:
$./mytar.shthisfile.tar
Thisappearstobeatarball.
$./mytar.shthatfile.gz
Atfirstglance,thisdoesnotappeartobeatarball.
好,成功运行,但是不太实用。
在使它更实用之前,先看一下上面使用的"
if"
语句。
语句中使用了一个布尔表达式。
在bash中,"
比较运算符检查字符串是否相等。
在bash中,所有布尔表达式都用方括号括起。
但是布尔表达式实际上测试什么?
让我们看一下左边。
根据前面所学的字符串截断知识,"
将从环境变量"
1"
包含的字符串开始部分除去最长的"
*."
匹配,并返回结果。
这将返回文件中最后一个"
."
之后的所有部分。
显然,如果文件以"
结束,结果将是"
,条件也为真。
您可能会想:
开始处的"
环境变量是什么。
很简单--$1是传给脚本的第一个命令行自变量,$2是第二个,以此类推。
好,已经回顾了功能,下面来初探"
If语句
与大多数语言一样,bash有自己的条件形式。
在使用时,要遵循以上格式;
即,将"
和"
then"
放在不同行,并使"
else"
和结束处必需的"
fi"
与它们水平对齐。
这将使代码易于阅读和调试。
除了"
if,else"
形式之外,还有其它形式的"
语句:
if[condition]
action
只有当condition为真时,该语句才执行操作,否则不执行操作,并继续执行"
之后的任何行。
elif[condition2]
action2
.
elif[condition3]
actionx
以上"
elif"
形式将连续测试每个条件,并执行符合第一个真条件的操作。
如果没有条件为真,则将执行"
操作,如果有一个条件为真,则继续执行整个"
if,elif,else"
语句之后的行。
接收自变量
在介绍性文章中的样本程序中,我们使用环境变量"
$1"
来引用第一个命令行自变量。
类似地,可以使用"
$2"
、"
$3"
等来引用传递给脚本的第二和第三个自变量。
这里有一个例子:
#!
/usr/bin/envbash
echonameofscriptis$0
echofirstargumentis$1
echosecondargumentis$2
echoseventeenthargumentis$17
echonumberofargumentsis$#
除以下两个细节之外,此例无需说明。
第一,"
$0"
将扩展成从命令行调用的脚本名称,"
$#"
将扩展成传递给脚本的自变量数目。
试验以上脚本,通过传递不同类型的命令行自变量来了解其工作原理。
有时需要一次引用所有命令行自变量。
针对这种用途,bash实现了变量"
$@"
,它扩展成所有用空格分开的命令行参数。
在本文稍后的"
for"
循环部分中,您将看到使用该变量的例子。
Bash编程结构
如果您曾用过如C、Pascal、Python或Perl那样的过程语言编程,则一定熟悉"
语句和"
循环那样的标准编程结构。
对于这些标准结构的大多数,Bash有自己的版本。
在下几节中,将介绍几种bash结构,并演示这些结构和您已经熟悉的其它编程语言中结构的差异。
如果以前编程不多,也不必担心。
我提供了足够的信息和示例,使您可以跟上本文的进度。
方便的条件语句
如果您曾用C编写过与文件相关的代码,则应该知道:
要比较特定文件是否比另一个文件新需要大量工作。
那是因为C没有任何内置语法来进行这种比较,必须使用两个stat()调用和两个stat结构来进行手工比较。
相反,bash内置了标准文件比较运算符,因此,确定“/tmp/myfile是否可读”与查看“$myvar是否大于4”一样容易。
下表列出最常用的bash比较运算符。
同时还有如何正确使用每一选项的示例。
示例要跟在"
之后。
例如:
if[-z"
$myvar"
echo"
myvarisnotdefined"
运算符描述示例
文件比较运算符
-efilename如果filename存在,则为真[-e/var/log/syslog]
-dfilename如果filename为目录,则为真[-d/tmp/mydir]
-ffilename如果filename为常规文件,则为真[-f/usr/bin/grep]
-Lfilename如果filename为符号链接,则为真[-L/usr/bin/grep]
-rfilename如果filename可读,则为真[-r/var/log/syslog]
-wfilename如果filename可写,则为真[-w/var/mytmp.txt]
-xfilename如果filename可执行,则为真[-L/usr/bin/grep]
filename1-ntfilename2如果filename1比filename2新,则为真[/tmp/install/etc/services-nt/etc/services]
filename1-otfilename2如果filename1比filename2旧,则为真[/boot/bzImage-otarch/i386/boot/bzImage]
字符串比较运算符(请注意引号的使用,这是防止空格扰乱代码的好方法)
-zstring如果string长度为零,则为真[-z"
-nstring如果string长度非零,则为真[-n"
string1=string2如果string1与string2相同,则为真["
onetwothree"
string1!
=string2如果string1与string2不同,则为真["
!
="
算术比较运算符
num1-eqnum2等于[3-eq$mynum]
num1-nenum2不等于[3-ne$mynum]
num1-ltnum2小于[3-lt$mynum]
num1-lenum2小于或等于[3-le$mynum]
num1-gtnum2大于[3-gt$mynum]
num1-genum2大于或等于[3-ge$mynum]
有