shell编程语法,shell编写函数
控制结构
Shell有一套结构控制。我们再次表明,它们与其他编程语言非常相似。
如果
if语句非常简单:它测试命令的结果,并有选择地执行一组语句:
if条件
然后
声明
其他
声明
船方不负担装货费用
使用if命令:
以下示例显示了if的常见用法。他会问一个问题,并根据这个问题来回答:
#!/bin/sh
“现在是早上吗?请回答是或否”
阅读时间
if[$ time of day=" yes "];然后
回声“早上好”
其他
附和“下午好”
船方不负担装货费用
出口0
这会产生以下输出:
$ ./if.sh
现在是早上吗?请回答是或否
是
早上好
这个脚本使用[命令来测试变量timeofday的值。这个结果将被if命令使用,它将执行不同的程序代码。
否则如果
但幸运的是,这个简单的脚本存在很多问题。除了“是”以外,他会把任何回答解释为“不是”。为了防止这样的问题,我们可以使用elif结构,这将允许我们在if执行else部分时添加第二个条件。
我们可以修改上面的脚本,在用户输入yes或no之外的内容时报告错误消息。我们的方法是使用elif而不是else来添加另一个条件。
#!/bin/sh
“现在是早上吗?请回答是或否”
阅读时间
if [ $timeofday="yes" ]
然后
回声“早上好”
elif[$ time of day=" no "];然后
附和“下午好”
其他
echo“对不起,$timeofday无法识别。输入是或否"
1号出口
船方不负担装货费用
出口0
工作原理:
这个例子和上一个类似,但是这一次,如果if的测试条件不为真,那么使用elif命令来测试变量的值。如果这些都不成功,将打印一条错误消息,脚本将退出并返回代码1,返回值可以作为另一个程序调用这个脚本来检查脚本是否成功。
变量的问题:
现在最明显的问题已经解决了,但是还有一个更小的问题潜伏着。我们可以尝试这个新脚本,但是如果我们只是输入回车(或者其他什么)而没有回答这个问题,我们将得到下面的错误消息:
[:=:应为一元运算符
出了什么样的差错?问题出在第一个if语句。当测试这个变量timeofday时,它包含一个空字符串。因此,if语句如下所示:
如果[="是"]
而这并不是一个可利用的条件。为了避免这样的问题,我们可以用双引号将变量括起来:
if [ "$timeofday"="yes" ]
向该测试传递空变量时:
if [ ""="是"]
我们的新脚本如下:
#!/bin/sh
“现在是早上吗?请回答是或否”
阅读时间
if [ "$timeofday"="yes" ]
然后
回声“早上好”
elif[" $ time of day "=" no "];然后
附和“下午好”
其他
echo“对不起,$timeofday无法识别。输入是或否"
1号出口
船方不负担装货费用
出口0
这是一个安全的脚本,用于输入答案。
为
我们使用for结构在任意一组字符串的值的范围内循环。它们可以简单地在程序中列出,或者更一般地,它们可以是文件名的外壳扩展结果。
语法如下:
对于值中变量
做
声明
完成的
在下面的示例中,值是普通字符,因此我们的脚本如下:
#!/bin/sh
对于酒吧fud 43中的foo
做
echo $foo
完成的
出口0
我们将得到以下结果:/for.sh
酒吧
飞悠达
43
工作原理:
这个例子创建了一个变量foo,并在for循环中每次赋予一个不同的值。因为Shell认为默认情况下所有变量都包含字符串,所以在示例中将使用43,就像字符串fud一样。
使用通配符。
如前所述,我们通常在for循环中使用文件名的Shell扩展名。我们的意思是在字符串值中使用通配符,并让Shell在运行时填充所有的值。
我们已经在我们最初的例子中看到了这个例子。该脚本使用外壳扩展名*,它成为当前目录中的所有文件名。在for循环中,每个变量依次用作变量$i的值。
让我们看看通配符的另一个例子。想象一下,如果我们想要打印当前目录中所有带有F字符的文件,并且我们知道我们所有的脚本都以。先令我们可以使用下面的脚本来完成我们的工作。
#!/bin/sh
对于以$(ls f*表示的文件。sh);做
lpr $文件
完成的
出口0
工作原理:
这里我们展示了$(命令)的语法。基本上,for命令使用的参数列表是由$()中包含的命令输出提供的。这个脚本扩展了f*。sh到所有匹配这个模式的内容。
正在…
因为Shell值默认被认为是一个字符串,所以for循环是遍历一系列字符的好选择,但是对于处理有一定次数的循环命令来说就有点笨拙了。
下面的例子向我们展示了当我们使用for循环在20个数字之间循环时是多么麻烦:
#!/bin/sh
用于foo in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
做
回声“我们又来了”
完成的
出口0
即使我们使用通配符来扩展,我们有时也会遇到所需循环数的不确定性。在这种情况下,我们可以使用while循环,其语法格式如下:
而条件确实如此
声明
完成的
以下是密码检测的一个示例:
#!/bin/sh
回显“输入密码”
阅读试试这个
while [ "$trythis "!=" secret "];做
回应“对不起,再试一次”
阅读试试这个
完成的
出口0
输出结果如下:
输入口令
密码
抱歉,再试一次
秘密
$
事实上,这并不是一种安全的检测密码的方法,但它可以很好地演示while语句的使用。do和done之间的语句将持续执行,直到我们的测试条件不再为真。在我们的输出示例中,我们检测到trythis的值不等于secret,循环将继续,直到$trythis等于secret。然后我们将在完成后执行语句。
通过将while语句与算术运算相结合,我们可以执行一定数量的循环操作,这比我们刚才看到的for的例子要简单得多。
#!/bin/sh
foo=1
while [ "$foo" -le 20 ]
做
回声“我们又来了”
foo=$(($foo 1))
完成的
出口0
工作原理:
这个脚本使用[命令检查foo的值,并将其与20进行比较。如果小于或等于20,它将执行循环体。在这个while循环中,(($foo 1))语法用于执行花括号中的算术运算,因此foo的值在每次循环后都会增加1。
因为foo不能是空字符串,所以在测试它的值时,我们不需要用双引号来保护它。我们这样做是因为这是一个好习惯。
直到
Until语句的语法格式如下:
直到条件
做
声明
完成的
这与while循环非常相似,只是测试条件相反。换句话说,until是在条件为真之前继续的循环,而不是在while条件为真时。
Until语句适用于我们希望循环直到某事发生的情况。举个例子,我们可以考虑以下情况:当我们在命令行输入另一个用户名登录时,会有铃声响起。
#!/bin/sh
直到who grep "$1" /dev/null
做
睡眠60
完成的
#现在响铃并宣布预期的用户。
回声-e //a
echo "**** $1刚刚登录**** "
出口0
我们将讨论案例结构。案例结构比我们已经讨论过的要复杂一些。其语法如下:
case变量在
图案.)语句;
图案.)语句;
.
environmental systems applications center 环境系统应用程序中心
这个结构看起来有点吓人,但是case结构可以让我们以一种奇怪的方式匹配变量的内容,并根据匹配的模式执行不同的语句。
这里,我们应该注意第一个模式行由双分号分隔。我们可以在第一个不同的模式之间放置多个不同的语句,所以我们需要使用双分号来标记一个模式的结束和另一个模式的开始。
case结构能够匹配多个模式并执行多个不同的相关语句,这使得这种结构能够很好地处理用户输入。展示case如何工作的最好方式是一个实际的例子,例如下面的例子:
现在我们可以编写一个新的脚本版本来处理用户输入。现在我们使用case结构,这可以使它更具选择性,并且可以处理未识别的输入。
#!/bin/sh
“现在是早上吗?请回答是或否”
阅读时间
案例“$timeofday”在
是)附和“早上好”;
没有)附和“下午好”;
y)附和“早上好”;
n)附和“下午好”;
*)附和“对不起,回答不识别”;
environmental systems applications center 环境系统应用程序中心
出口0
工作原理:
在这个脚本中,我们在每个场景的输入中使用多个字符串,因此case将为每个可能的语句检测一些不同的字符串。这将使我们的脚本更短,在实践中更具可读性。我们还展示了*通配符的使用,即使它可能与意外情况相匹配。比如用户输入never,这会匹配n*显示下午好,这不是我们想要的。在这里,我们应该注意,如果*通配符使用引号,它将不起作用。
最后,如果我们想让这个脚本可重用,但是在使用最后一个匹配模式时需要不同的返回值,这里我们也添加了集合结构。
#!/bin/sh
“现在是早上吗?请回答是或否”
阅读时间
案例“$timeofday”在
是是是是)
回声“早上好”
echo“今天一大早就起床了”
;
[nN]*)
附和“下午好”
;
*)
回显“对不起,无法识别答案”
附和“请回答是或不是”
1号出口
;
environmental systems applications center 环境系统应用程序中心
出口0
工作原理:
为了展示一种不同的模式匹配方法,我们改变了no的用法。这里要注意的是,我们把最明显的匹配放在前面,一般的匹配放在后面。这个更重要,因为case会先执行他找到的第一个匹配模式,但不是最好的。如果我们把*)放在前面,那么匹配将是
还有一点我们也要注意,esac之前;这是可选的。这里不像C语言。
为了使大小写匹配更强大,我们可以使用以下形式:
[yY] [Yy][Ee][Ss])
当允许多个答案时,这将限制允许的字符,并且比*通配符具有更大的控制力。
列表:
有时我们会连接一系列命令。例如,我们在执行语句之前需要多个约束,如下例所示:
if[-f this _ file];然后
if [ -f那个_文件];然后
if[-f the _ other _ file];然后
回显“所有文件都存在,并且正确”
船方不负担装货费用
船方不负担装货费用
船方不负担装货费用
或者我们希望一系列条件为真,比如:
if[-f this _ file];然后
foo="True "
elif[-f that _ file];然后
foo="True "
elif[-f the _ other _ file];然后
foo="True "
其他
foo="False "
船方不负担装货费用
if[" $ foo "=" True "];然后
回显“其中一个文件存在”
船方不负担装货费用
虽然我们可以用多个if语句来实现,但我们会发现这样的结果相当烦人。在Shell中,有一对特殊的结构可以用来处理像这个列这样的命令:and列和OR列。通常,它们会一起使用,但这里我们将分别研究它们的语法。
和列:
AND列结构可以执行一系列命令。只有前面的命令成功了,才能执行后面的命令。他的语法如下:
陈述1陈述2陈述3.
这些命令将从左边开始执行每个命令,如果返回值为真,将执行右边的下一个命令。这个进程会继续执行一个返回值为false的命令,之后这个列表中的命令就不再执行了。将检测前一个命令的执行结果。每个语句都可以独立执行,这将允许我们在一个列表中执行不同的命令,如下例所示。如果此列表中的所有命令都成功执行,则这是一个
在下面的脚本中,我们创建file_one(首先检查它是否存在,如果不存在就先创建这个文件),然后删除file_two。然后,AND命令行将检测每个文件是否存在,并在命令之间输入文本。
#!/bin/sh
触摸文件_一
rm -f文件_二
如果[ -f file_one ]回显“你好”[ -f file_two ]回显“那里”
然后
回显“如果”
其他
回显“在else中”
船方不负担装货费用
出口0
如果我们运行这个脚本,我们将得到以下结果:
你好
在else
和touch rm命令来确保当前目录中的文件处于已知状态。该表然后执行[-f file_one]语句,这将是成功的,因为我们刚刚保证了这个文件的存在。因为前面的语句是成功的,所以执行echo命令。这也将是成功的(echo通常返回true)。不过第三条语句会在后面执行,[-f file_two]。该语句不会成功,因为该文件已不存在。因为最后一个命令失败,所以不会执行最后一个echo语句。该表的结果为false,因为该列表中的一个命令失败,所以if语句将执行else条件。
或表格:
OR表I的结构可以让我们执行一系列命令,直到一个命令成功,然后就不会再执行更多的命令了。他的语法结构如下:
语句1 语句2 语句3 .
从左开始依次执行每条语句。如果其返回值为false,将执行下一条语句。这个过程将一直持续到一个语句返回真值,这样以后就不会执行下一个列表中的命令了。
除了执行下一条语句的条件之外,列表与列表非常相似。
下面举个例子:
#!/bin/sh
rm -f文件_一
if[-f file _ one] echo“hello” echo“there”
然后
回显“如果”
其他
回显“在else中”
船方不负担装货费用
出口0
我们将获得以下输出:
你好
在如果
工作原理:
脚本中的前两行只是为脚本的其余部分设置文件。第一个命令[-f file_one]将返回false,因为该文件不再存在。然后将执行echo命令。令人惊讶的是,这将返回一个真值,因此命令列表中将不再有命令。因为该列表中有一个返回真值的命令,所以if语句成功。
这两个结构的结果是要执行的最后一条语句的结果。
如果我们想要测试多个条件,我们可以像在C语言中一样使用这些列表类的结构。我们可以把这两种结构结合起来;
[ -f file_one ]命令为真命令为假
如果测试成功,将执行第一条语句;否则,将执行第二条语句。我们可以试验这些不常见的列表结构,一般来说,我们需要使用花括号来强制执行的顺序。
语句块:
如果我们想在一个地方使用多个语句,但只允许使用一个语句,例如,在AND或OR列表中,我们可以用花括号将它们括起来,形成一个语句块。
功能:
我们可以在Shell中定义函数。如果我们想要形成任意大小的脚本文件,我们可以使用函数来构造我们的代码。
要定义一个函数,我们只需写下函数名,后跟一个空括号,并用花括号将语句括起来:
函数名(){
声明
}
比如下面这个简单的函数:
#!/bin/sh
foo() {
echo“函数foo正在执行”
}
回显“脚本开始”
富(中国姓氏)
回显“脚本已结束”
出口0
执行这个脚本,我们将得到以下输出:
脚本开始
函数foo正在执行
脚本结尾
工作原理:
这个脚本是从顶部执行的,所以这里没有区别。但是当他找到foo(){结构时,他就会知道这里定义了一个函数调用。他会存储foo指向一个函数的情况,在}之后继续执行。当执行foo()行时,Shell知道前面定义的函数将在这里执行。当这个函数完成时,脚本将继续在foo行上执行。
我们必须在调用一个函数之前定义它。这种风格有一些类似Pascal的函数定义,但不同的是需要在Shell前面声明。这不是问题,因为所有的脚本都是从顶层执行的,所以我们只需要简单的把这些函数中所有我们想在第一次调用之前定义的函数放进去,就可以解决函数需要先定义再调用的问题。
当一个函数被调用时,脚本中的参数,$*,$@,$#,$1,$2等。将被函数中的参数替换。这也是我们读取传递给函数的参数的方式。当函数结束时,它们将恢复到以前的值。
我们可以使用return命令让函数返回一个数值。通常的做法是将函数返回的字符串存储在一个变量中,以便在函数完成后可以使用。
这里,我们应该注意,我们可以使用local关键字在Shell函数中定义局部变量。那么这个变量只会在这个函数中起作用。否则,一个函数可以访问另一个函数中定义的全局变量。如果局部变量与全局变量同名,则该变量将覆盖函数中的全局变量。例如,下面的例子:
#!/bin/sh
sample_text="全局变量"
foo() {
local sample_text="局部变量"
echo“函数foo正在执行”
echo $sample_text
}
回显“脚本开始”
echo $sample_text
富(中国姓氏)
回显“脚本已结束”
echo $sample_text
出口0
如果没有使用返回命令,当执行最后一个命令时,函数将返回返回状态。
在下面的例子中,我们将展示如何向函数传递参数,以及函数如何返回真或假的结果。我们可以用一个名字作为参数来调用这个脚本。
1我们在脚本头后面定义了一个yes_or_no函数:
#!/bin/sh
yes_or_no() {
echo“你叫$*?”
虽然是真的
做
echo -n "输入是或否: "
阅读x
案例“$x”在
y 是)返回0;
n no)返回1;
*)回应“回答是或不是”
environmental systems applications center 环境系统应用程序中心
完成的
}
2以下是本节目的主要部分:
echo "原始参数是$* "
如果是_或_否" $1 "
然后
echo“嗨$1,好名字”
其他
echo“没关系”
船方不负担装货费用
出口0
当我们运行这个脚本时,我们将得到以下输出:
$ ./我的名字里克尼尔
原始参数是瑞克尼尔
你的名字是瑞克吗?
输入是或否:是
嗨,瑞克,好名字
$
工作原理:
当这个脚本被执行时,yes_or_no函数被定义,但是这个函数不运行。在if语句中,这个脚本执行我们定义的函数,用传递给原始脚本的第一个参数Rick替换$1,然后将句子的其余部分作为参数传递给函数。该函数使用这些参数,并将它们存储在位置参数$1、$2等中。并向调用方提供返回值。根据这个返回值,if结构语句可以执行相应的操作。
正如我们所看到的,Shell有丰富的控制和条件结构语句。我们需要学习Shell中内置的一些命令。这样就可以解决一些真正的程序问题,不会被编译器看到。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。