LINUX学习:shell的编程结构体(函数、条件结构、循环结构)
《LINUX学习:shell的编程结构体(函数、条件结构、循环结构)》要点: 本文目次: 1.1 shell函数 1.2 条件布局:if 1.3 条件布局:case 1.4 条件布局:select 1.5 循环布局:for 1.6 循环布局:while 1.7 循环布局:until 1.8 exit、break、continue和return ? 1.1 shell函数在shell中,函数可以被当作命令一样执行,它是命令的组合布局体.可以将函数看成是一个普通命令或者一个小型脚本. 起首给出几个关于函数的结论:
函数的语法布局:
上面的语法结构中定义了一个名为name的函数,关键字function是可选的,如果使用了function关键字,则name后的括号可以省略.compound-cmd是函数体,通常使用大括号{}包抄,由于历史原因,大括号本身也是关键字,所以为了不产生歧义,函数体必须和大括号使用空格、制表符、换行符分隔开来.还可以指定可选的函数重定向功能,这样当函数被调用的时候,指定的重定向也会被执行. 例如:界说一个名为rm的函数,该函数会将传递的所有文件移动到"~/backup"目录下,目的是替代rm命令,避免误删除的危险操作. [root@linuxidc ~]# function rm () { [ -d ~/rmbackup ] || mkdir ~/rmbackup;/bin/mv -f $@ ~/rmbackup; } &>/dev/null 在挪用rm函数时,只需是给rm函数传递参数即可.例如,要删除/tmp/a.log. [root@linuxidc ~]# rm /tmp/a.log
在执行函数时,会将执行可能输出的信息重定向到/dev/null中. 为了让函数在子shell(例如脚本)中也可以使用,使用export的"-f"选项将其导出为全局函数.撤消函数的导出则使用export的"-n"选项. export -f rm export -n rm 关于shell函数,还有几个必要说明的知识点:
? 1.2 条件布局:if语法布局:
if的判断很简单,一切都以返回状态码是否为0为判决条件.如果test-commands1执行后的退出状态码为0(不是其执行结果为0),则执行commands1部分的结构体,不然如果test-commands2返回0则执行commands2部分的结构体,如果都不满足,则执行commands3的结构体. 常见的test-commands有几种类型: (1).一条普通的敕令.只要该敕令退出状态码为0,则执行then后的语句体.例如: if echo haha &>/dev/null;then echo go;fi (2).测试语句.例如test、[]、[[]]. (3).使用逻辑运算符,包含!、&&和||.该特性主要是为普通命令而提供,因为测试语句自身就支持逻辑运算.所以,对于测试语句就提供了两种写法,一种是将逻辑运算符作为测试语句的一部分,一种是将逻辑运算符作为if语句的一部分.例如: if ! id "$name" &>/dev/null;then echo "$name" miss;fi if ! [ 3 -eq 3 ];then echo go;fi if [ ! 3 -eq 3 ];then echo go;fi if [ 3 -eq 3 ] && [ 4 -eq 4 ] ;then echo go;fi if [ 3 -eq 3 -a 4 -eq 4 ];then echo go;fi if [[ 3 -eq 3 && 4 -eq 4 ]];then echo go;fi 注意,在if语句中使用()不能改变优先级,而是让括号内的语句成为命令列表并进入子shell运行.因此,要改变优先级时,必要在测试语句中完成. ? 1.3 条件布局:case语法布局:
sysV作风的服务启动脚本是shell脚本中使用case语句最典型案例.例如: case "$1" in start) start;; stop) stop;; restart) restart;; reload | force-reload) reload;; status) status;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}" exit 2 esac 从上面的示例中,可以看出一些结论: (1).case中的每个小分句都以双分号";;"结尾,但最后一个小分句的双分号可以省略.实际上,小分句除了使用";;"结尾,还可以使用";&"和";;&"结尾,只不外意义不同,它们用的不多,不外为了文章完整性,稍后还是给出说明. (2).每个小分句中的pattern部分都使用括号"()"包抄,只不过左括号"("不是必须的. (3).每个小分句的pattern支持通配符模式匹配(不是正则匹配模式,因此只有3种通配元字符:"*"、"?"和[...]),其中使用"|"分隔多个通配符pattern,表现满足其中一个pattern即可.例如"([yY] | [yY][eE][sS]])"表现即可以输入单个字母的y或Y,还可以输入yes三个字母的任意大小写格式. set -- y;case "$1" in ([yY]|[yY][eE][sS]) echo right;;(*) echo wrong;;esac 其中"set -- string_list"的作用是将输入的string_list依照IFS分隔后分别赋值给位置变量$1、$2、$3...,因此此处是为$1赋值字符"y". (4).最后一个小分句使用的pattern是"*",表示无法匹配前面所有小分句时,将匹配该小分句.一般最后一个小分句都会使用"*"避免case语句无法匹配的情况,在shell脚本中,此小分句一般用于提示用户脚本的使用办法,即给出脚本的Usage. (5).附加一个结论:如果任何模式都不匹配,该命令的返回状态是零;不然,返回最后一个被执行的命令的返回值. 如果小分句不是使用双分号";;"结尾,而是使用";&"或";;&"结尾,则case语句的行为将转变. ◇ ";;"结尾符号表现小分句执行完成后立即退出case语句. ◇ ";&"表现继续执行下一个小分句中的command部分,而无需进行匹配动作,并由此小分句的结尾符号来决定是否继续操作下一个小分句. ◇ ";;&"表现继续向后(不止是下一个,而是一直向后)匹配小分句,如果匹配成功,则执行对应小分句中的command部分,并由此小分句的结尾符号来决定是否继续向后匹配. 示例如下: set -- y case "$1" in ([yY]|[yY][eE][sS]) echo yes;& ([nN]|[nN][oO]) echo no;; (*) echo wrong;; esac yes no 在此示例中,$1能匹配第一个小分句,但第一个小分句的结尾符号为";&",所以无需判断地直接执行第二个小分句的"echo no",但第二个小分句的结尾符号为";;",于是直接退出case语句.因此,即使$1无法匹配第二个小分句,case语句的成果中也输出了"yes"和"no". set -- y case "$1" in ([yY]|[yY][eE][sS]) echo yes;;& ([nN]|[nN][oO]) echo no;; (*) echo wrong;; esac yes wrong 在此示例中,但第一个小分句的结尾符号为";;&",所以继续向下匹配,第二个小分句未匹配胜利,直到第三个小分句才被匹配上,于是执行第三个小分句中的"echo wrong",但第三个小分句的结尾符号为";;",于是直接退出case语句.所以,结果中输出了"yes"和"wrong". ? 1.4 条件布局:selectshell中提供菜单选择的条件判断布局.例如: [root@linuxidc ~]# select fname in cat dog sheep mouse;do echo your choice: "$REPLY) $fname";break;done 1) cat 2) dog 3) sheep 4) mouse #? 3 # 在此选择序号3 your choice: "3) sheep" # 将输出序号3对应的内容 语法布局:
它的布局几乎和for循环的布局相同.有以下几个要点: (1).in关键词后的word将根据IFS变量进行分割,分割后的每一项都进行编号,作为菜单序号被输出,如果省略in word,则等价于"in $@",即将地位变量的内容作为菜单项. (2).当选择菜单序号后,该序号的内容将保留到变量name中,并且所输入的内容(一般是序号值,例如上面的例子中输入的3,但不规定一定要输入序号值,例如随便输入几个字符)保留保留到特殊变量REPLY中. (3).每次输入选择后,select语句都将重置,如果输入的菜单序号存在,则cmd_list会重新执行,变量name也将重置.如果没有break敕令,则select语句会一直运行,如果遇到break敕令,将退出select语句. 仍旧是上面的示例:但不是用break [root@linuxidc ~]# select fname in cat dog sheep mouse;do echo your choice: "$REPLY) $fname";done 1) cat 2) dog 3) sheep 4) mouse #? 2 your choice: "2) dog" #? habagou # 随意输入几个字符 your choice: "habagou) " # 变量fname被重置为空,变量REPLY被赋予了输入的值habagou #? 2 3 your choice: "2 3) " #? ^C # 直到杀失落进程select才结束 ? 1.5 循环布局:forfor循环再shell脚本中应用极其广泛,它有两种语法布局:
结构一中:将扩展in word,然后依照IFS变量对word进行分割,并依次将分割的单词赋值给变量name,每赋值一次,执行一次循环体cmd_list,然后再继续将下一个单词赋值给变量name,直到所有变量赋值结束.如果省略in word,即展开位置变量并依次赋值给变量name.注意,如果word中使用引号包围了某些单词,这引号包围的内容被分割为一个单词. 例如: [root@linuxidc ~]# for i in 1 2 3 4;do echo $i;done 1 2 3 4 [root@linuxidc ~]# for i in 1 2 "3 4";do echo $i;done 1 2 3 4 结构二中:该结构的expr部分只支持数学计算和比拟.首先计算expr1,再判断expr2的返回状态码,如果为0,则执行cmd_list,并将计算expr3的值,并再次判断expr2的状态码.直到expr2的返回状态码不为0,循环结束. 例如: [root@linuxidc ~]# for ((i=1;i<=3;++i));do echo $i;done 1 2 3 [root@linuxidc ~]# for ((i=1,j=3;i<=3 && j>=2;++i,--j));do echo $i $j;done 1 3 2 2 ? 1.6 循环结构:while使用while循环尽量要让条件运行到可以退出循环,不然无限循环.一般都在命令体部分加上变量的改变行为. 语法布局:
首先执行test_cmd_list中的命令,当test_cmd_list的最后一个命令的状态码为0时,将执行一次cmd_list,然后回到循环的开首继续执行test_cmd_list.只有test_cmd_list中最后一个测试命令的状态码非0时,循环才会退出. 例如:计算1到10的算术和. [root@linuxidc ~]# let i=1,sum=0;while [ $i -le 10 ];do let sum=sum+i;let ++i;done;echo $sum 55 在此例中,test_cmd_list中只有一个命令[ $i -le 10 ],所以它的状态直接决议整个循环何时退出. test_cmd_list中可以是多个命令,但千万要考虑清楚,是否要让决定退出循环的测试命令处在列表的尾部,不然将进入无线循环. [root@linuxidc ~]# let i=1,sum=0;while echo $i;[ $i -le 10 ];do let sum=sum+i;let ++i;done;echo $sum 1 2 3 4 5 6 7 8 9 10 11 55 对付while循环,有另外两种常见的写法: (1).test_cmd_list部门使用一个冒号":"或者true命令,使得while进入无限循环.
(2).使用read命令从标准输入中按行读取值,然后保留到变量line中(既然是read命令,所以可以保留到多个变量中),读取一行是一个循环. 由于标准输入既可以来源于重定向,也可以来源于管道(本色还是重定向),所以有几种常见的写法: 写法一:使用管道通报内容,这是最烂的写法
写法二:
写法三:从文件中读取内容
既然是读取尺度输入,于是还可以衍生出几种写法:
尽管写法有多种,但注意,它们并不等价.办法一中使用的是管道符号,这使得while语句在子shell中执行,这意味着while语句内部设置的变量、数组、函数等在循环外部都不再生效.例如: #!/bin/bash echo "abc xyz" | while read line do new_var=$line done echo the variable new_var is null: $new_var? 该脚本的执行成果中,$new_var的值将为空. 使用除写法一外的任意一种写法,在while循环外部都能继续获得while内的环境.例如,使用写法二的here string取代写法一: #!/bin/bash while read line do new_var=$line done <<< "abc xyz" echo the variable new_var is null: $new_var? 由此可以发现,在上面的5种写法中,年夜众使用的最广泛写法一其实是最烂的一种,如果没注意写法一中while是在子shell运行,很可能会一直疑惑,为什么在while循环里设置好的变量或数组在循环一结束就成了空值呢. ? 1.7 循环结构:untiluntil和while循环基本一致,所分歧的仅仅只是test_cmd_list的意义. 语法布局:
首先断定test_cmd_list中的最后一个命令,如果状态码为非0,则执行一次cmd_list,然后再返回循环的开头再次执行test_cmd_list,直到test_cmd_list的最后一个命令状态码为0时,才退出循环. 和while不同的是,当判断test_cmd_list最后一个命令的状态满足退出条件时直接退出循环,也便是说循环是在test_cmd_list最后一个命令处退出的. 例如: [root@linuxidc ~]# i=5;until echo haha;[ "$i" -eq 0 ];do let --i;echo $i;done haha 4 haha 3 haha 2 haha 1 haha 0 haha ? 1.8 exit、break、continue和returnexit [n] ? ? ? ? :退出当前shell,在脚本中应用则表现退出整个脚本(子shell).其中数值n表现退出状态码. break [n] ? ? :退出整个循环,包含for、while、until和select语句.其中数值n表示退出的循环层次. continue [n] :退出当前循环进入下一次循环.n表现继续执行第n次循环. return [n] ? ? :退出整个函数.n表现函数的退出状态码. 本文永远更新链接地址: 《LINUX学习:shell的编程结构体(函数、条件结构、循环结构)》是否对您有启发,欢迎查看更多与《LINUX学习:shell的编程结构体(函数、条件结构、循环结构)》相关教程,学精学透。编程之家PHP学院为您提供精彩教程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |