文档库 最新最全的文档下载
当前位置:文档库 › AIX SHELL基本编程

AIX SHELL基本编程

AIX SHELL基本编程
AIX SHELL基本编程

根据网上资料下载的,整理了拿来看,顺便发个同行

学习如何使用bash 脚本语言编程,将使Linux 的日常交互更有趣和有生产力,同时还可以利用那些已熟悉和喜爱的标准UNIX 概念(如管道和重定向)。

在此三部分系列中,Daniel Robbins 将以示例指导您如何用bash 编程。他将讲述非常基本的知识(这使此系列十分适合初学者),并在后续系列中逐步引入更高级特性。

您可能要问:为什么要学习Bash 编程?好,以下是几条令人信服的理由:

已经在运行它

如果查看一下,可能会发现:您现在正在运行bash。因为bash 是标准Linux shell,并用于各种目的,所以,即使更改了缺省shell,bash 可能仍在系统中某处运行。因为bash 已在运行,以后运行的任何bash 脚本都天生是有效利用内存的,因为它们与任何已运行的bash 进程共享内存。如果正在运行的工具可以胜任工作,并且做得很好,为什么还要装入一个500K 的解释器?

已经在使用它

不仅在运行bash,实际上,您每天还在与bash 打交道。它总在那里,因此学习如何最大限度使用它是有意义的。这样做将使您的bash 经验更有趣和有生产力。但是为什么要学习bash 编程?很简单,因为您已在考虑如何运行命令、CPing 文件以及管道化和重定向输出。为什么不学习一种语言,以便使用和利用那些已熟悉和喜爱的强大省时的概念?命令shell 开启了UNIX 系统的潜能,而bash 正是这个Linux shell。它是您和机器之间的高级纽带。增长bash 知识吧,这将自动提高您在Linux 和UNIX 中的生产力-- 就那么简单。

Bash 困惑

以错误方式学习bash 令人十分困惑。许多新手输入"man bash" 来查看bash 帮助页,但只得到非常简单和技术方面的shell 功能性描述。还有人输入"info bash"(来查看GNU 信息文档),只能得到重新显示的帮助页,或者(如果幸运)略为友好的信息文档。

尽管这可能使初学者有些失望,但标准bash 文档无法满足所有人的要求,它只适合那些已大体熟悉shell 编程的人。帮助页中确实有很多极好的技术信息,但对初学者的帮助却有限。

这就是本系列的目的所在。在本系列中,我将讲述如何实际使用bash 编程概念,以便编写自己的脚本。与技术描述不同,我将以简单的语言为您解释,使您不仅知道事情做什么,还知道应在何时使用。在此三部分系列末尾,您将可以自己编写复杂的bash 脚本,并可以自如地使用bash 以及通过阅读(和理解)标准bash 文档来补充知识。让我们开始吧。

环境变量

在bash 和几乎所有其它shell 中,用户可以定义环境变量,这些环境变量在以ASCII 字符串存储。环境变量的最便利之处在于:它们是UNIX 进程模型的标准部分。这意味着:环境变量不仅由shell 脚本独用,而且还可以由编译过的标准程序使用。当在bash 中“导出”环境变量时,以后运行的任何程序,不管是不是shell 脚本,都可以读取设置。一个很

好的例子是vipw 命令,它通常允许root 用户编辑系统口令文件。通过将EDITOR 环境

变量设置成喜爱的文本编辑器名称,可以配置vipw,使其使用该编辑器,而不使用vi,如

果习惯于xemacs 而确实不喜欢vi,那么这是很便利的。

在bash 中定义环境变量的标准方法是:

以上命令定义了一个名为"myvar" 的环境变量,并包含字符串"This is my environment

variable!"。以上有几点注意事项:第一,在等号"=" 的两边没有空格,任何空格将导致错误(试一下看看)。第二个件要注意的事是:虽然在定义一个字时可以省略引号,但是当定

义的环境变量值多于一个字时(包含空格或制表键),

第三,虽然通常可以用双引号来替代单引号,但在上

例中,这样做会导致错误。为什么呢?因为使用单引

号禁用了称为扩展的bash 特性,其中,特殊字符和

字符系列由值替换。例如,"!" 字符是历史扩展字符,

bash 通常将其替换为前面输入的命令。(本系列文章

中将不讲述历史扩展,因为它在bash 编程中不常用。

有关历史扩展的详细信息,请参阅bash 帮助页中的

“历史扩展”一节。)尽管这个类似于宏的功能很便利,

但我们现在只想在环境变量后面加上一个简单的感叹号,而不是宏。

现在,让我们看一下如何实际使用环境变量。这有一个例子:

语中叫做“变量扩展”。但是,这样做将怎样:

地说,bash 变量扩展设施陷入了困惑。它无法识别要扩展哪一个变量:$m、$my、$myvar 、

如您所见,当环境变量没有与周围文本明显分开时,可以用花括号将它括起。虽然$myvar 可以更快输入,并且在大多数情况下正确工作,但${myvar} 却能在几乎所有情况下正确通过语法分析。除此之外,二者相同,将在本系列的余下部分看到变量扩展的两种形式。请记住:当环境变量没有用空白(空格或制表键)与周围文本分开时,请使用更明确的花括号形

式。

回想一下,我们还提到过可以“导出”变量。当导出环境变量时,它可以自动地由以后运行的任何脚本或可执行程序环境使用。shell 脚本可以使用shell 的内置环境变量支持“到达”环境变量,而C 程序可以使用getenv() 函数调用。这里有一些C 代码示例,输入并编译它们-- 它将帮助我们从C 的角度理解环境变量:

将上面的代码保存到文件myenv.c 中,然后发出以下命令进行编译:

现在,目录中将有一个可执行程序,它在运行时将打印EDITOR 环境变量的值(如果有值的话)。这是在我机器上运行时的情况:

啊... 因为没有将EDITOR 环境变量设置成任何值,所以C 程序得到一个空字符串。让我

虽然希望myenv 打印值"xemacs",但是因为还没有导出环境变量,所以它却没有很好地工作。这次让它正确工作:

现在,如您亲眼所见:不导出环境变量,另一个进程(在本例中是示例C 程序)就看不到环境变量。顺便提一句,如果愿意,可以在一行定义并导出环境变量,如下所示:

这与两行版本的效果相同。现在该演示如何使用unset 来除去环境变量:

截断字符串概述

截断字符串是将初始字符串截断成较小的独立块,它是一般shell 脚本每天执行的任务之

一。很多时候,shell 脚本需要采用全限定路径,并找到结束的文件或目录。虽然可以用bash

编码实现(而且有趣),但标准basename UNIX 可执行程序可以极好地完成此工作:

Basename 是一个截断字符串的极简便工具。它的相关命令dirname 返回basename 丢弃的“另”一部分路径。Array

命令替换

需要知道一个简便操作:如何创建一个包含可执行命令结果的环境变量。这很容易:Array

上面所做的称为“命令替换”。此例中有几点需要指出。在第一行,简单地将要执行的命令以

反引号括起。那不是标准的单引号,而是键盘中通常位于Tab 键之上的单引号。可以用

如您所见,bash 提供多种方法来执行完全一样的操作。使用命令替换可以将任何命令或命令管道放在` ` 或$( ) 之间,并将其分配给环境变量。真方便!下面是一个例子,演示如

何在命令替换中使用管道:

象专业人员那样截断字符串

尽管basename 和dirname 是很好的工具,但有时可能需要执行更高级的字符串“截断”,而不只是标准的路径名操作。当需要更强的说服力时,可以利用bash 内置的变量扩展功能。已经使用了类似于${MYVAR} 的标准类型的变量扩展。但是bash 自身也可以执行一些便利的字符串截断。看一下这些例子:

在第一个例子中,输入了${MYVAR##*fo}。它的确切含义是什么?基本上,在${ } 中输入环境变量名称,两个##,然后是通配符("*fo")。然后,bash 取得MYVAR,找到从字符串"foodforthought.jpg" 开始处开始、且匹配通配符"*fo" 的最长子字符串,然后将其从字符串的开始处截去。刚开始理解时会有些困难,为了感受一下这个特殊的"##" 选项如何工作,让我们一步步地看看bash 如何完成这个扩展。首先,它从"foodforthought.jpg" 的开始处搜索与"*fo" 通配符匹配的子字符串。以下是检查到的子字符串:

f

fo MATCHES *fo

foo

food

foodf

foodfo MATCHES *fo

foodfor

foodfort

foodforth

foodfortho

foodforthou

foodforthoug

foodforthought

foodforthought.j

foodforthought.jp

foodforthought.jpg

在搜索了匹配的字符串之后,可以看到bash 找到两个匹配。它选择最长的匹配,从初始字符串的开始处除去,然后返回结果。

上面所示的第二个变量扩展形式看起来与第一个相同,但是它只使用一个"#" -- 并且bash 执行几乎同样的过程。它查看与第一个例子相同的子字符串系列,但是bash 从初始字符串除去最短的匹配,然后返回结果。所以,一查到"fo" 子字符串,它就从字符串中除去"fo",然后返回"odforthought.jpg"。

这样说可能会令人十分困惑,下面以一简单方式记住这个功能。当搜索最长匹配时,使用##(因为## 比# 长)。当搜索最短匹配时,使用#。看,不难记吧!等一下,怎样记住应该使用'#' 字符来从字符串开始部分除去?很简单!注意到了吗:在美国键盘上,shift-4 是"$",它是bash 变量扩展字符。在键盘上,紧靠"$" 左边的是"#"。这样,可以看到:"#" 位于"$" 的“开始处”,因此(根据我们的记忆法),"#" 从字符串的开始处除去字符。您可能要问:如何从字符串末尾除去字符。如果猜到我们使用美国键盘上紧靠"$" 右边的字符

正如您所见,除了将匹配通配符从字符串末尾除去之外,% 和%% 变量扩展选项与# 和

在此例中,使用"%%" 或"%" 并不重要,因为只能有一个匹配。还要记住:如果忘记了应该使用"#" 还是"%",则看一下键盘上的3、4 和5 键,然后猜出来。

可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。试着在

这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。

应用字符串截断

现在我们已经学习了所有截断字符串的知识,下面写一个简单短小的shell 脚本。我们的脚本将接受一个文件作为自变量,然后打印:该文件是否是一个tar 文件。要确定它是否是tar 文件,将在文件末尾查找模式".tar"。如下所示:

mytar.sh -- 一个简单的脚本

要运行此脚本,将它输入到文件mytar.sh 中,然后输入"chmod 755 mytar.sh",生成可执行文件。然后,如下做一下tar 文件试验:

好,成功运行,但是不太实用。在使它更实用之前,先看一下上面使用的"if" 语句。语句中使用了一个布尔表达式。在bash 中,"=" 比较运算符检查字符串是否相等。在bash 中,所有布尔表达式都用方括号括起。但是布尔表达式实际上测试什么?让我们看一下左边。根据前面所学的字符串截断知识,"${1##*.}" 将从环境变量"1" 包含的字符串开始部分除去最长的"*." 匹配,并返回结果。这将返回文件中最后一个"." 之后的所有部分。显然,如果文件以".tar" 结束,结果将是"tar",条件也为真。

您可能会想:开始处的"1" 环境变量是什么。很简单-- $1 是传给脚本的第一个命令行自变量,$2 是第二个,以此类推。好,已经回顾了功能,下面来初探"if" 语句。

If 语句

与大多数语言一样,bash 有自己的条件形式。在使用时,要遵循以上格式;即,将"if" 和"then" 放在不同行,并使"else" 和结束处必需的"fi" 与它们水平对齐。这将使代码易于阅读和调试。除了"if,else" 形式之外,还有其它形式的"if" 语句:

只有当condition为真时,该语句才执行操作,否则不执行操作,并继续执行"fi" 之后的任何行。

以上"elif" 形式将连续测试每个条件,并执行符合第一个真条件的操作。如果没有条件为真,则将执行"else" 操作,如果有一个条件为真,则继续执行整个"if,elif,else" 语句之后的行。

回页首

下一次

我们已经学习了最基本的bash 功能,现在要加快脚步,准备编写一些实际脚本。在下一篇中,将讲述循环概念、函数、名称空间和其它重要主题。然后,将准备好编写一些更复杂的脚本。在第三篇中,将重点讲述一些非常复杂的脚本和功能,以及几个bash 脚本设计选项。再见

********************************************88

在前一篇bash 的介绍性文章中,Daniel Robbins 为您讲解了脚本语言的一些基本元素和使用bash 的原因。在本文(即第二部分)中,Daniel 继续前一篇的内容,并讲解条件(if-then) 语句、循环和更多的bash 基本结构。

我们先看一下处理命令行自变量的简单技巧,然后再看看bash 基本编程结构。

接收自变量

在介绍性文章中的样本程序中,我们使用环境变量"$1" 来引用第一个命令行自变量。类似地,可以使用"$2"、"$3" 等来引用传递给脚本的第二和第三个自变量。这里有一个例子:

除以下两个细节之外,此例无需说明。第一,"$0" 将扩展成从命令行调用的脚本名称,"$#" 将扩展成传递给脚本的自变量数目。试验以上脚本,通过传递不同类型的命令行自变量来了解其工作原理。

有时需要一次引用所有命令行自变量。针对这种用途,bash 实现了变量"$@",它扩展成所有用空格分开的命令行参数。在本文稍后的"for" 循环部分中,您将看到使用该变量的例子。

Bash 编程结构

如果您曾用过如C、Pascal、Python 或Perl 那样的过程语言编程,则一定熟悉"if" 语句和"for" 循环那样的标准编程结构。对于这些标准结构的大多数,Bash 有自己的版本。在下几节中,将介绍几种bash 结构,并演示这些结构和您已经熟悉的其它编程语言中结构的差异。如果以前编程不多,也不必担心。我提供了足够的信息和示例,使您可以跟上本文的进度。

方便的条件语句

如果您曾用 C 编写过与文件相关的代码,则应该知道:要比较特定文件是否比另一个文件新需要大量工作。那是因为 C 没有任何内置语法来进行这种比较,必须使用两个stat() 调用和两个stat 结构来进行手工比较。相反,bash 内置了标准文件比较运算符,因此,确定“/tmp/myfile 是否可读”与查看“$myvar 是否大于4”一样容易。

下表列出最常用的bash 比较运算符。同时还有如何正确使用每一选项的示例。示例要跟在"if" 之后。例如:

运算符描述示例

文件比较运算符

-e filename如果filename存在,则为真[ -e /var/log/syslog ]

-d filename如果filename为目录,则为真[ -d /tmp/mydir ]

-f filename如果filename为常规文件,则为真[ -f /usr/bin/grep ]

-L filename如果filename为符号链接,则为真[ -L /usr/bin/grep ]

-r filename如果filename可读,则为真[ -r /var/log/syslog ]

-w filename如果filename可写,则为真[ -w /var/mytmp.txt ]

-x filename如果filename可执行,则为真[ -L /usr/bin/grep ]

filename1-nt 如果filename1比filename2新,则为[ /tmp/install/etc/services -nt /etc/services ]

filename2真

filename1-ot filename2如果filename1比filename2旧,则为

[ /boot/bzImage -ot

arch/i386/boot/bzImage ]

字符串比较运算符(请注意引号的使用,这是防止空格扰乱代码的好方法)

-z string如果string长度为零,则为真[ -z "$myvar" ]

-n string如果string长度非零,则为真[ -n "$myvar" ]

string1= string2如果string1与string2相同,则为真 [ "$myvar" = "one two three" ]

string1!= string2如果string1与string2不同,则为真 [ "$myvar" != "one two three" ]

算术比较运算符

num1-eq num2等于[ 3 -eq $mynum ]

num1-ne num2不等于[ 3 -ne $mynum ]

num1-lt num2小于[ 3 -lt $mynum ]

num1-le num2小于或等于[ 3 -le $mynum ]

num1-gt num2大于[ 3 -gt $mynum ]

num1-ge num2大于或等于[ 3 -ge $mynum ]

有时,有几种不同方法来进行特定比较。例如,以下两个代码段的功能相同:

上面两个比较执行相同的功能,但是第一个使用算术比较运算符,而第二个使用字符串比较运算符。

字符串比较说明

大多数时候,虽然可以不使用括起字符串和字符串变量的双引号,但这并不是好主意。为什么呢?因为如果环境变量中恰巧有一个空格或制表键,bash 将无法分辨,从而无法正常工作。这里有一个错误的比较示例:

在上例中,如果myvar 等于"foo",则代码将按预想工作,不进行打印。但是,如果myvar 等于"foo bar oni",则代码将因以下错误失败:

在这种情况下,"$myvar"(等于"foo bar oni")中的空格迷惑了bash。bash 扩展"$myvar" 之后,代码如下:

因为环境变量没放在双引号中,所以bash 认为方括号中的自变量过多。可以用双引号将字符串自变量括起来消除该问题。请记住,如果养成将所有字符串自变量用双引号括起的习

以上代码将按预想工作,而不会有任何令人不快的意

外出现。

循环结构:"for"

好了,已经讲了条件语句,下面该探索bash 循环结构了。我们将从标准的"for" 循环开始。

这里有一个简单的例子:

#!/usr/bin/env bash

for x in one two three four

do

echo number $x

done

输出:

number one

number two

number three

number four

发生了什么?"for" 循环中的"for x" 部分定义了一个名为"$x" 的新环境变量(也称为循环控制变量),它的值被依次设置为"one"、"two"、"three" 和"four"。每一次赋值之后,执行一次循环体("do" 和"done" 之间的代码)。在循环体内,象其它环境变量一样,使用标准的变量扩展语法来引用循环控制变量"$x"。还要注意,"for" 循环总是接收"in" 语句之后的某种类型的字列表。在本例中,指定了四个英语单词,但是字列表也可以引用磁盘上的文件,甚至文件通配符。看看下面的例子,该例演示如何使用标准shell 通配符:

#!/usr/bin/env bash

for myfile in /etc/r*

do

if [ -d "$myfile" ]

then

echo "$myfile (dir)"

else

echo "$myfile"

fi

done

输出:

/etc/rc.d (dir)

/etc/resolv.conf

/etc/resolv.conf~

/etc/rpc

以上代码列出在/etc 中每个以"r" 开头的文件。要做到这点,bash 在执行循环之前首先取得通配符/etc/r*,然后扩展它,用字符串/etc/rc.d /etc/resolv.conf /etc/resolv.conf~

/etc/rpc 替换。一旦进入循环,根据myfile 是否为目录,"-d" 条件运算符用来执行两个不同操作。如果是目录,则将"(dir)" 附加到输出行。

Bash 将在所有正确位置上执行通配符和环境变量扩展,并可能创建一个非常长的字列表。虽然所有通配符扩展示例使用了绝对路径,但也可以使用相对路径,如下所示:

在上例中,bash 相对于当前工作目录执行通配符扩展,就象在命令行中使用相对路径一样。研究一下通配符扩展。您将注意到,如果在通配符中使用绝对路径,bash 将通配符扩展成一个绝对路径列表。否则,bash 将在后面的字列表中使用相对路径。如果只引用当前工作目录中的文件(例如,如果输入"for x in *"),则产生的文件列表将没有路径信息的前缀。请记住,可以使用"basename" 可执行程序来除去前面的路径信息,如下所示:

当然,在脚本的命令行自变量上执行循环通常很方便。这里有一个如何使用本文开始提到的"$@" 变量的例子:

#!/usr/bin/env bash

for thing in "$@"

do

echo you typed ${thing}.

done

输出:

$ allargs hello there you silly

you typed hello.

you typed there.

you typed you.

you typed silly.

Shell 算术

在学习另一类型的循环结构之前,最好先熟悉如何执行shell 算术。是的,确实如此:可以使用shell 结构来执行简单的整数运算。只需将特定的算术表达式用"$((" 和"))" 括起,bash 就可以计算表达式。这里有一些例子:

更多的循环结构:"while" 和"until"

通常使用"While" 语句来循环一定次数,比如,下例将循环10 次:

可以看到,上例使用了算术表达式来使条件最终为假,并导致循环终止。

"Until" 语句提供了与"while" 语句相反的功能:只要特定条件为假,它们就重复。下面是一个与前面的"while" 循环具有同等功能的"until" 循环:

Case 语句

Case 语句是另一种便利的条件结构。这里有一个示例片段:

在上例中,bash 首先扩展"${x##*.}"。在代码中,"$x" 是文件的名称,"${x##.*}" 除去文件中最后句点后文本之外的所有文本。然后,bash 将产生的字符串与")" 左边列出的值做比较。在本例中,"${x##.*}" 先与"gz" 比较,然后是"bz2",最后是"*"。如果"${x##.*}" 与这些字符串或模式中的任何一个匹配,则执行紧接")" 之后的行,直到";;" 为止,然后bash 继续执行结束符"esac" 之后的行。如果不匹配任何模式或字符串,则不执行任何代码行,在这个特殊的代码片段中,至少要执行一个代码块,因为任何不与"gz" 或"bz2" 匹配的字符串都将与"*" 模式匹配。

函数与名称空间

在bash 中,甚至可以定义与其它过程语言(如Pascal 和C)类似的函数。在bash 中,函数甚至可以使用与脚本接收命令行自变量类似的方式来接收自变量。让我们看一下样本函数定义,然后再从那里继续:

tarview() {

echo -n "Displaying contents of $1 "

if [ ${1##*.} = tar ]

then

echo "(uncompressed tar)"

tar tvf $1

elif [ ${1##*.} = gz ]

then

echo "(gzip-compressed tar)"

tar tzvf $1

elif [ ${1##*.} = bz2 ]

then

echo "(bzip2-compressed tar)"

cat $1 | bzip2 -d | tar tvf -

我们在上面定义了一个名为 "tarview" 的函数,它接收一个自变量,即某种类型的 tar 文件。在执行该函数时,它确定自变量是哪种 tar 文件类型(未压缩的、gzip 压缩的或 bzip2 压缩的),打印一行信息性消息,

然后显示 tar 文件的内容。应该如下调用上面的函数(在输入、粘贴或找到该函数后,从脚本或命令行调用它):

如您所见,可以使用与引用命令行自变量同样的机制来在函数定义内部引用自变量。另外,将把 "$#" 宏扩展成包含自变量的数目。唯一可能不完全相同的是变量 "$0",它将扩展成字符串 "bash"(如果从 shell 交互运行函数)或调用函数的脚本名称。

名称空间

经常需要在函数中创建环境变量。虽然有可能,但是还有一个技术细节应该了解。在大多数编译语言(如 C )中,当在函数内部创建变量时,变量被放置在单独的局部名称空间中。因此,如果在 C 中定义一个名为 myfunction 的函数,并在该函数中定义一个名为 "x" 的自变量,则任何名为 "x" 的全局变量(函数之外的变量)将不受它的印象,从而消除了负作用。

在 C 中是这样,但在 bash 中却不是。在 bash 中,每当在函数内部创建环境变量,就将其添加到 全局名称空间。这意味着,该变量将重写函数之外的全局变量,并在函数退出

运行此脚本时,它将输出"one two three three",这显示了在函数中定义的"$myvar" 如何影响全局变量"$myvar",以及循环控制变量"$x" 如何在函数退出之后继续存在(如果"$x" 全局变量存在,也将受到影响)。

在这个简单的例子中,很容易找到该错误,并通过使用其它变量名来改正错误。但这不是正确的方法,解决此问题的最好方法是通过使用"local" 命令,在一开始就预防影响全局变量的可能性。当使用"local" 在函数内部创建变量时,将把它们放在局部名称空间中,并且不会影响任何全局变量。这里演示了如何实现上述代码,以便不重写全局变量:

#!/usr/bin/env bash

myvar="hello"

myfunc() {

local x

local myvar="one two three"

for x in $myvar

do

echo $x

done

}

myfunc

echo $myvar $x

此函数将输出"hello" -- 不重写全局变量"$myvar","$x" 在myfunc 之外不继续存在。在函数的第一行,我们创建了以后要使用的局部变量x,而在第二个例子(local myvar="one two three"") 中,我们创建了局部变量myvar,同时为其赋值。在将循环控制变量定义为局部变量时,使用第一种形式很方便,因为不允许说:"for local x in $myvar"。此函数不影响任何全局变量,鼓励您用这种方式设计所有的函数。只有在明确希望要修改全局变量时,才不应该使用"local"。

使用Bash shell脚本进行功能测试

在使您的应用程序成型过程中节省时间和精力

级别:初级

Angel Rivera (rivera@https://www.wendangku.net/doc/067035543.html,), 软件工程师, VisualAge TeamConnection, IBM

2001 年3 月01 日

功能测试是软件开发的一个关键部分-- 而已经装入Linux 的Bash 可以帮您轻而易举地完成功能测试。在本文中,Angel Rivera 将说明如何运用Bash shell 脚本通过行命令来执行Linux 应用程序的功能测试。由于此脚本依赖于命令行的返回码,因而您不能将这种方法运用于GUI 应用程序

功能测试是开发周期的一个阶段,在这个阶段中将测试软件应用程序以确保软件的函数如预期的那样,同时能正确处理代码中错误。此项工作通常在单个模块的单元测试结束之后,在负载/重压条件下整个产品的系统测试之前进行的。

市场上有许多测试工具提供了有助于功能测试的功能。然而,首先要获取它们,然后再安装、配置,这将占用您宝贵的时间和精力。Bash 可以帮您免去这些烦琐的事从而可以加快测试的进程。

使用Bash shell 脚本进行功能测试的优点在于:

?Bash shell 脚本已经在Linux 系统中安装和配置好了。不必再花时间准备它。

?可以使用由Linux 提供的文本编辑器如vi 创建和修改Bash shell 脚本。不需要再为创建测试程序而获取专门的工具。

?如果已经知道了如何开发Bourne 或Korn shell 脚本,那对于如何运用Bash shell 脚本已经足够了。对您来说,学习曲线已不存在了。

?Bash shell 提供了大量的编程构造用于开发从非常简单到中等复杂的脚本。

将脚本从Korn 移植到Bash 时的建议

如果已有现成的Korn shell 脚本,而想要将它们移植到Bash,就需要考虑下列情况:

?Korn 的"print" 命令在Bash 中不能使用;而是改为使用"echo" 命令。

?需要将脚本的第一行:

#!/usr/bin/ksh

修改成:

#!/bin/bash

创建Bash shell 脚本进行功能测试

这些基本的步骤和建议适用于许多在Linux 上运行的客户机/服务器应用程序。

1. 记录运行脚本的先决条件和主要步骤

2. 将操作分成若干个逻辑组

3. 基于一般方案制定执行步骤

4. 在每个shell 脚本中提供注释和说明

5. 做一个初始备份以创建基准线

6. 检查输入参数和环境变量

7. 尝试提供"usuage" 反馈

8. 尝试提供一个“安静”的运行模式

9. 当出现错误时,提供一个函数终止脚本

10. 如可能,提供可以执行单个任务的函数

11. 当显示正在生成的输出时,捕获每个脚本的输出

12. 在每个脚本内,捕获每个行命令的返回码

13. 计算失败事务的次数

14. 在输出文件中,突出显示错误消息,以便于标识

15. 如有可能,“实时”生成文件

16. 在执行脚本的过程中提供反馈

17. 提供脚本执行的摘要

18. 提供一个容易解释的输出文件

19. 如有可能,提供清除脚本及返回基准线的方法

下面详细讲述了每一条建议以及用于说明问题的脚本。若要下载此脚本,请参阅本文后面的参考资料部分。

1. 记录运行脚本的先决条件和主要步骤

记录,尤其是以有自述标题的单个文件(例如"README-testing.txt")记录功能测试的主要想法是很重要的,包括,如先决条件、服务器和客户机的设置、脚本遵循的整个(或详细的)步骤、如何检查脚本的成功/失败、如何执行清除和重新启动测试。

2. 将操作分成若干个逻辑组

如果仅仅执行数量非常少的操作,可以将它们全部放在一个简单的shell 脚本中。

但是,如果需要执行一些数量很多的操作,那最好是将它们分成若干个逻辑集合,例如将一些服务器操作放在一个文件而将客户机操作放在在另一个文件中。通过这种方法,划分适当的颗粒度来执行测试和维护测试。

3. 基于一般方案制定执行步骤

一旦决定对操作进行分组,需要根据一般方案考虑执行操作的步骤。此想法是模拟实际生活中最终用户的情形。作为一个总体原则,只需集中测试80% 最常调用函数的20% 用法即可。

例如,假设应用程序要求3 个测试组以某个特定的顺序排列。每个测试组可以放在一个带有自我描述文件名(如果可能)的文件中,并用号码来帮助识别每个文件的顺序,例如:

4. 在每个shell 脚本中提供注释和说明

在每个shell 脚本的头文件中提供相关的注释和说明是一个良好的编码习惯。这样的话,当另一个测试者运行该脚本时,测试者就能清楚地了解每个脚本中测试的范围、所有先决条件和警告。

下面是一个Bash 脚本"test-bucket-1" 的示例。

#!/bin/bash

#

# Name: test-bucket-1

#

# Purpose:

# Performs the test-bucket number 1 for Product X.

# (Actually, this is a sample shell script,

# which invokes some system commands

# to illustrate how to construct a Bash script)

#

# Notes:

# 1) The environment variable TEST_VAR must be set

# (as an example).

# 2) To invoke this shell script and redirect standard

# output and standard error to a file (such as

# test-bucket-1.out) do the following (the -s flag

# is "silent mode" to avoid prompts to the user):

#

# ./test-bucket-1 -s 2>&1 | tee test-bucket-1.out

#

# Return codes:

# 0 = All commands were successful

# 1 = At least one command failed, see the output file

# and search for the keyword "ERROR".

#

########################################################

5. 做一个初始备份以创建基准线

您可能需要多次执行功能测试。第一次运行它时,也许会找到脚本或进程中的一些错误。因

相关文档