Shell基础语法——流程控制、函数
admin
2024-03-05 19:39:23
0

Shell 流程控制

选择结构:分别是 if else 语句和 case in 语句。

if 语句:

语法1:(if 语句)

if  condition
thenstatement(s)
fi

condition是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。

语法2:(if 语句)

if  condition;  thenstatement(s)
fi

请注意 condition 后边的分号;,当 if 和 then 位于同一行的时候,这个分号是必须的,否则会有语法错误。

语法3:(if else 语句)

if  condition
thenstatement1
elsestatement2
fi

如果 condition 成立,那么 then 后边的 statement1 语句将会被执行;否则,执行 else 后边的 statement2 语句。

语法4:(if elif else 语句)

if  condition1
thenstatement1
elif condition2
thenstatement2
elif condition3
thenstatement3
……
elsestatementn
fi

注意,if 和 elif 后边都得跟着 then。

case in 语句:

case expression inpattern1)statement1;;pattern2)statement2;;pattern3)statement3;;……*)statementn
esac

case、in 和 esac 都是 Shell 关键字,expression 表示表达式,pattern 表示匹配模式。

  • expression 既可以是一个变量、一个数字、一个字符串,还可以是一个数学计算表达式,或者是命令的执行结果,只要能够得到 expression 的值就可以。
  • pattern 可以是一个数字、一个字符串,甚至是一个简单的正则表达式。

case 会将 expression  的值与 pattern1、pattern2、pattern3 逐个进行匹配:

  • 如果 expression 和某个模式(比如 pattern2)匹配成功,就会执行这模式(比如 pattern2)后面对应的所有语句(该语句可以有一条,也可以有多条),直到遇见双分号;;才停止;然后整个 case 语句就执行完了,程序会跳出整个 case 语句,执行 esac 后面的其它语句。
  • 如果 expression 没有匹配到任何一个模式,那么就执行*)后面的语句(*表示其它所有值),直到遇见双分号;;或者esac才结束。*)相当于多个 if 分支语句中最后的 else 部分。

case in 和正则表达式

case in 的 pattern 部分支持简单的正则表达式,具体来说,可以使用以下几种格式:

格式说明
*表示任意字符串。
[abc]表示 a、b、c 三个字符中的任意一个。比如,[15ZH] 表示 1、5、Z、H 四个字符中的任意一个。
[m-n]表示从 m 到 n 的任意一个字符。比如,[0-9] 表示任意一个数字,[0-9a-zA-Z] 表示字母或数字。
|表示多重选择,类似逻辑运算中的或运算。比如,abc | xyz 表示匹配字符串 "abc" 或者 "xyz"。

对*)的几点说明:

Shell case in 语句中的*)用来“托底”,万一 expression 没有匹配到任何一个模式,*)部分可以做一些“善后”工作,或者给用户一些提示。

可以没有*)部分。如果 expression 没有匹配到任何一个模式,那么就不执行任何操作。

除最后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。最后一个分支可以写;;,也可以不写,因为无论如何,执行到 esac 都会结束整个 case in 语句。

最后一个分支*)并不是什么语法规定,它只是一个正则表达式,*表示任意字符串,所以不管 expression 的值是什么,*)总能匹配成功。

循环结构:

while 循环:

while condition
dostatements
done

 while 循环的执行流程为:

  • 先对 condition 进行判断,如果该条件成立,就进入循环,执行 while 循环体中的语句,也就是 do 和 done 之间的语句。这样就完成了一次循环。
  • 每一次执行到 done 的时候都会重新判断 condition 是否成立,如果成立,就进入下一次循环,继续执行 do 和 done 之间的语句,如果不成立,就结束整个 while 循环,执行 done 后面的其它 Shell 代码。
  • 如果一开始 condition 就不成立,那么程序就不会进入循环体,do 和 done 之间的语句就没有执行的机会。

until循环

unti 循环和 while 循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。 

until condition
dostatements
done

 一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

for 循环

语法1:

for var in value_list
docommand1command2...commandN
done

当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。in 列表可以包含替换、字符串和文件名。

in列表是可选的,如果不用它,for循环使用命令行的位置参数。

对 value_list 的说明

取值列表 value_list 的形式有多种,你可以直接给出具体的值,也可以给出一个范围,还可以使用命令产生的结果,甚至使用通配符

  • 直接给出具体的值:可以在 in 关键字后面直接给出具体的值,多个值之间以空格分隔 ;

  • 给出一个取值范围:{start..end}  注意中间用两个点号相连,这种形式只支持数字和字母;

  • 使用命令的执行结果:使用反引号``或者$()都可以取得命令的执行结果;

  • 使用 Shell 通配符:一种精简化的正则表达式,通常用来匹配目录或者文件,而不是文本;

  • 使用特殊变量:例如 $#、$*、$@、$?、$$ 等,可省略,省略后效果与$@一样。

语法2: 

for((exp1; exp2; exp3))
dostatements
done

 for 循环中的三个表达式

for 循环中的 exp1(初始化语句)、exp2(判断条件)和 exp3(自增或自减)都是可选项,都可以省略(但分号;必须保留)。

  1. 省略 exp1:将exp1移到了 for 循环的外面。
  2. 省略 exp2:可以在循环体内部使用 break 关键字强制结束循环
  3. 省略了 exp3:可在循环体中加入修改变量的语句。

 无限循环

while :
docommand
done
#------------------
while true
docommand
done
#------------------
for (( ; ; ))

 select in循环

select in 循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。
select in 是 Shell 独有的一种循环,非常适合终端(Terminal)这样的交互场景

select variable in value_list
dostatements
done

 举例:

#!/bin/bash
echo "What is your favourite OS?"
select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
doecho $name
done
echo "You have selected $name"运行结果:
What is your favourite OS?
1) Linux
2) Windows
3) Mac OS
4) UNIX
5) Android
#? 4↙
You have selected UNIX
#? 1↙
You have selected Linux
#? 9↙
You have selected
#? 2↙
You have selected Windows
#?^D

#?用来提示用户输入菜单编号;^D表示按下 Ctrl+D 组合键,它的作用是结束 select in 循环。

运行到 select 语句后,取值列表 value_list 中的内容会以菜单的形式显示出来,用户输入菜单编号,就表示选中了某个值,这个值就会赋给变量 variable,然后再执行循环体中的 statements(do 和 done 之间的部分)。

每次循环时 select 都会要求用户输入菜单编号,并使用环境变量 PS3 的值作为提示符,PS3 的默认值为#?,修改 PS3 的值就可以修改提示符。

如果用户输入的菜单编号不在范围之内,例如上面我们输入的 9,那么就会给 variable 赋一个空值;如果用户输入一个空值(什么也不输入,直接回车),会重新显示一遍菜单。

注意,select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到 break 语句,或者按下 Ctrl+D 组合键才能结束循环。

跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue

在C语言、C++、C#、Python、Java 等大部分编程语言中,break 和 continue 只能跳出当前层次的循环,内层循环中的 break 和 continue 对外层循环不起作用;但是 Shell 中的 break 和 continue 却能够跳出多层循环,也就是说,内层循环中的 break 和 continue 能够跳出外层循环。

break 关键字 

语法:break n

n 表示跳出循环的层数,如果省略 n,则表示跳出当前的整个循环。break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环。

例1:如果 break 后面不跟数字的话,表示跳出当前循环,对于有两层嵌套的循环,就得使用两个 break 关键字。

#!/bin/bash
i=0
while ((++i)); do  #外层循环if((i>4)); thenbreak  #跳出外层循环fij=0;while ((++j)); do  #内层循环if((j>4)); thenbreak  #跳出内层循环fiprintf "%-4d" $((i*j))doneprintf "\n"
done

例2:在 break 后面跟一个数字,让它一次性地跳出两层循环

#!/bin/bash
i=0
while ((++i)); do  #外层循环j=0;while ((++j)); do  #内层循环if((i>4)); thenbreak 2  #跳出内外两层循环fiif((j>4)); thenbreak  #跳出内层循环fiprintf "%-4d" $((i*j))doneprintf "\n"
done

修改后的代码将所有 break 都移到了内层循环里面。读者需要重点关注break 2这条语句,它使得程序可以一次性跳出两层循环,也就是先跳出内层循环,再跳出外层循环。

continue 关键字

语法:continue n

n 表示循环的层数:

  • 如果省略 n,则表示 continue 只对当前层次的循环语句有效,遇到 continue 会跳过本次循环,忽略本次循环的剩余代码,直接进入下一次循环。
  • 如果带上 n,比如 n 的值为 2,那么 continue 对内层和外层循环语句都有效,不但内层会跳过本次循环,外层也会跳过本次循环,其效果相当于内层循环和外层循环同时执行了不带 n 的 continue。这么说可能有点难以理解,稍后我们通过代码来演示。

【实例】使用 continue 跳出多层循环,请看下面的代码: 

#!/bin/bash
for((i=1; i<=5; i++)); dofor((j=1; j<=5; j++)); doif((i*j==12)); thencontinue 2fiprintf "%d*%d=%-4d" $i $j $((i*j))doneprintf "\n"
done

 从运行结果可以看出,遇到continue 2时,不但跳过了内层 for 循环,也跳过了外层 for 循环。

Shell函数 

 Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。

Shell 函数定义的语法格式如下:

function name() {statements[return value]
}

对各个部分的说明:

  • function是 Shell 中的关键字,专门用来定义函数;
  • name是函数名;
  • statements是函数要执行的代码,也就是一组语句;
  • return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。

{ }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。

简化写法

# 省略function 关键字:
name() {statements[return value]
}# 省略函数名后面的小括号:
function name {statements[return value]
}

函数参数

Shell 中的函数在定义时不能指明参数,但是在调用时却可以传递参数。

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...  $10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

特殊字符用来处理参数:

参数处理说明
$#传递到脚本或函数的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

函数调用

调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可:

name

如果传递参数,那么多个参数之间以空格分隔:

name param1 param2 param3

不管是哪种形式,函数名字后面都不需要带括号。

Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数。

相关内容

热门资讯

颈肩腰腿疼反复不好? 坐久了脖子僵硬、低头久了肩膀酸痛、弯腰起身腰就刺痛,如今颈肩腰腿疼早已不是老年人的专属问题,越来越多...
段永平,再捐1万股茅台 综合自 财联社、投行圈子 近日,据多家媒体报道,知名企业家、投资家段永平向安福县慈善会无偿捐赠所持贵...
原创 “... 下一个独角兽,是谁? 北京,又交出了一份答卷。 最新发布的《中国独角兽企业发展报告2026》显示,...
产能不饱和还要扩产,手握巨款仍... 近日,千红制药(002550.SZ)披露了《向不特定对象发行可转换公司债券并在主板上市募集说明书(二...
原创 大... 科创板有点太过于热闹了,此前有长鑫科技和长江存储,这两家公司上市的消息刚刚消停了一会,这不今天早上又...
国内一级市场投融资周数据超11... 财联社5月31日消息,本周(5.23-5.29)国内统计口径内共发生112起投融资事件,较上周114...
原创 狂... 最近盯盘的时候,发现了一个让人后背发凉、值得警惕的数据:美国具备市场风向标意义的 30 年期长期国债...
原创 站... 特朗普觊觎加拿大的领土,而卡尼则直指美国霸权的根基,他心里很清楚,想要制衡对方,最有力的力量非中国莫...
可买车买房!亚辉龙拟向员工提供... 北京商报讯(记者 董亮)5月29日晚间,亚辉龙(688575)披露公告称,拟按《员工借款管理制度》的...
财政部在香港发行60亿元人民币... 新华社北京5月30日电 《中国证券报》30日刊发文章《财政部在香港发行60亿元人民币绿色主权债券》。...
全球顶级创作者为何齐聚上海——... 5月30日晚,2026互联网优质内容创作盛典(TOP CREATORS GALA,简称TCG)将在上...
全部卸任!百亿基金经理,刚刚发... 爆款产品基金经理宣布卸任! 5月30日,德邦基金发布公告,旗下基金经理雷涛因个人原因卸任其管理的全部...
利润腰斩也要卷AI!小米模型永... 作者 | 华卫 今日,小米宣布永久性翻新整个模型定价体系。价格调整公告称,MiMo-V2.5 系列 ...
超声电子2025年研发投入2.... 超声电子(000823)披露2025年年度报告。报告期内,公司全年研发投入达2.88亿元,同比增长0...
原创 一... 雷达财经出品 文|丁禹 编|孟帅 净利润由盈转亏、毛利率暴跌,一季度的理想汽车过得并不“舒坦”。 今...
尾盘,生变!02513,“一字... 【导读】“老登”反击,尾盘多股异动,智谱直线闪崩 中国基金报记者 泰勒 大家好啊,今天的市场,又让泰...
菊乐股份北交所IPO过会,九年... 蓝鲸新闻5月29日讯(记者 朱欣悦)5月29日,北交所上市委2026年第53次审议会议结果显示,四川...
原创 人... 2023年,联合国人口司报告:印度人口超过了中国。中国丢掉了“世界第一人口大国”的帽子,马路上娃娃车...