shell 流程控制
来源:互联网 发布:雪梨淘宝店质量 编辑:程序博客网 时间:2024/05/22 10:49
【前言】
之前写过一篇文章叫做 shell脚本的基础入门,既然已经入门了,那今天就来说说shell编程的进阶。
我们知道,shell脚本可以用来帮助我们更快的提高工作效率,而在工作中,很多时候的工作的简单而复杂的。什么叫做简单而复杂?简单是说他的操作性很简单,只是一行命令或者两行命令搞定,而复杂是说可能由于工作的需要性,有时候这一条或者两行命令需要我们重复执行十遍百遍,如果说人工去一遍一遍的执行,那你也就基本可以放弃这个岗位了,所以我们需要借助脚本来帮助我们实现。
举个简单的经典面试题吧:计算1+2+…+100的值。
想想我们之前是怎么做的?在之前的学习中,我是这么做的
[root@localhost ~]# echo {1..100}|tr ' ' '+ '|bc5050
虽然这也是一种方法,显然这并不是面试官出题的意图,体现不了你的真实水平,那么我们就引入今天的正题:shell编程高级进阶。
【进入正题】
过程性的编程语言分为三类:
顺序执行
选择执行
循环执行
所谓顺序执行,即在一个shell脚本中,按照你所写的命令一条一条 的按照顺序执行:
[root@localhost app]# vim test.sh#!/bin/bash echo i am $USER useradd Tom #创建用户id Tom #显示用户信息[root@localhost app]# ./test.sh #执行脚本i am rootuid=1021(Tom) gid=1021(Tom) groups=1021(Tom)
以上这种普遍的脚本就是顺序执行,但是在一些情况下,我们需要让脚本进行自行判断,符合某种条件则执行某种操作,这种就是选择执行。首先来看if …..else…..语句。
if..else语句
单分支
if 条件;then
条件为真的分支代码
fi
双分支
if 条件;then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 条件1;then
条件为真的分支代码
elif 条件2;then
条件为真的分支代码
else
条件为假的分支代码
fi
判断条件:每一次遇到为“真”的条件,执行其分支语句,然后结束整个进程。当然if语句是可以嵌套的。
练习:实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;并给用户设置随机生成的8位密码,初始化登陆时提示用户修改密码,显示添加的用户的id号等信息。
[root@localhost bin]# vim createuser.sh#/bin/bashpsd=`cat /dev/random |tr -dc '[0-9A-Za-z]'|head -c 8` ------>将随机生成的8位密码存储到变量中read -p "please input a username: " usrid $usr &> /dev/null ------>判断用户是否存在if [ $? -eq 0 ];then ------>如存在则显示用户已存在,不存在则执行else的分支代码 echo $usr has existelse useradd $usr echo $psd | passwd --stdin $usr &>/dev/null echo $usr:$psd >> /app/passwdrandom ------>将用户名及密码输出到一个文件中 chage -d 0 $usr ------>强制用户下次登陆时修改密码 echo "user $usr is created!" id $usrfiunset psd usr ------>删除变量[root@localhost bin]# ./createuser.shplease input a username: xiaowanguser xiaowang is created!uid=1022(xiaowang) gid=1022(xiaowang) groups=1022(xiaowang)
case语句
case 变量引用 in
条件1)
分支1
;;
条件2)
分支2
;;
esac
练习:编写脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息。
[root@localhost bin]# vim yesorno.sh read -p "please input yes or no: " ansvar1=` echo $ans |tr -t '[a-z]' '[A-Z]'` ------>将用户输入转换为大写字母,以防用户输入含有大写字母case $var1 inY|YES) echo you input yes ;;N|NO) echo you input no ;;*) echo please input a right answer ;;esac[root@localhost bin]# ./yesorno.sh please input yes or no: noyou input no[root@localhost bin]# ./yesorno.sh please input yes or no: yesyou input yes[root@localhost bin]# ./yesorno.sh please input yes or no: hahaplease input a right answer
选择执行常用的就是以上两种了,都是比较简单易理解的,接下来看循环执行,自身觉得比较烧脑的来了~~~
循环执行是为了节省人力,将执行的命令进行循环操作以达到目的,首先介绍for循环。
for 循环
先来看for循环的语法:
for: for NAME [in WORDS … ] ; do COMMANDS; done
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done 两种格式写法
NAME:指变量
[in WORDS … ]:执行列表
do COMMANDS:执行操作
done:结束符
执行条件:依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束。
还记得上面我们说的计算1+2+…+100的值吗?我们这次使用循环来计算~~~
[root@localhost bin]# sum=0;for i in {1..100};do let sum=sum+i;let i++;done;echo sum is $sumsum is 5050 #写入脚本也同样适用[root@localhost bin]# sum=0;for ((i=1;i<=100;i++));do let sum+=i;done;echo sum is $sumsum is 5050
for循环有多种写法,以上这种是数字循环,还有以下几种:
字符循环#!/bin/bashfor i in `ls /app/`do echo $i is file namedone#!/bin/bashlist="aa bb cc dd"for i in $listdo echo $idone路径循环for file in /app/*do echo "$file is file name" done
练习:打印99乘法表
思路:使用两层循环,内层的循环变量引用外层的循环变量。
[root@localhost bin]# vim for7_9x9.sh for i in {1..9}do for j in `seq 1 $i`;do echo -e "$i*$j=$[i*j] \c\t" done echodoneunset i j[root@localhost bin]# ./for7_9x9.sh 1*1=12*1=2 2*2=43*1=3 3*2=6 3*3=94*1=4 4*2=8 4*3=12 4*4=165*1=5 5*2=10 5*3=15 5*4=20 5*5=256*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=367*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=498*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=649*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
while 循环
语法格式:
while: while COMMANDS; do COMMANDS; done
while ((: while(( exp1; exp2; exp3 )); do COMMANDS; done 两种格式写法
判断条件:当判断条件为真时,则会一直执行;判断条件为假,则会终止循环。
那么有一种语法就可以实现死循环:
while true ;do echo true;done
由于true
的返回值永远为真,那么使用while就会一直循环。
练习:编写脚本,求100以内所有正奇数之和。
[root@localhost bin]# vim while1_jishusum.sh sum=0i=1while [ $i -le 100 ];do if [ $[$i%2] -eq 1 ];then let sum=sum+i fi let i++doneecho 100以内所有正奇数之和是 $sumunset i sum[root@localhost bin]# ./while1_jishusum.sh 100以内所有正奇数之和是 2500
练习:打印国际棋盘
思路:国际棋盘为八行八列,以两个空格为一个盘格,打印空格底色实现棋盘效果。
[root@localhost bin]# vim whileqipan.sh#!/bin/bashi=1while ((i<=8));do j=1 while ((j<=8));do varnum=$[$[i+j]%2] ------>计算行数和列数之和取余的值 if [ $varnum -eq 0 ];then echo -n -e "\033[41m \033[0m\033" ------>打印两个红色 elif [ $varnum -eq 1 ];then echo -n -e "\033[47m \033[0m\033" ------>打印两个白色 fi let j++ done let i++ echodoneunset i j
*until循环
until和while的格式上是相同的,但是用法上正好相反。它的判断条件是当条件判断为真时退出循环。
*continue
continue为循环控制语句,用于循环体中,表示结束某一个符合条件的循环,结束的只是当前轮的循环。
*break
break用法同continue相同,不同的是它结束的是整个循环。
*select 用法
select 常用于菜单,常与case组合达到菜单的效果。
使用PS3,显示用户界面提示符,相等于read -p 的效果
$REPLY变量的select的内置变量,用来引用用户输入。
[root@localhost bin]# vim menu47.sh PS3="请选择菜单编号:"select menu in 米饭 炒菜 面条 馒头 退出docase $REPLY in1|4) echo this price is 20 ;;2|3) echo this price is 12 ;;5) echo byebye exit 2 ;;*) echo no this number ! echo $menuesacdone[root@localhost bin]# menu47.sh 1) 米饭2) 炒菜3) 面条4) 馒头5) 退出请选择菜单编号:1 this price is 20请选择菜单编号:4this price is 20请选择菜单编号:2this price is 12请选择菜单编号:5byebye[root@localhost bin]#
【函数】
函数类似于shell脚本,里面存放了一些指令,与脚本不同的是,这些函数可以统一放在一个文件里,在脚本中可以调用这些函数,重复使用,效率较高。
函数的格式:
function function_name () { 一些指令,即函数体}关键字function表示定义一个函数,可以省略,后面是函数的名字,()是格式要求,函数体中具体写一些需要完成的指令。
调用:调用一个函数直接调用定义的函数名即可,与shell命令的用法相同。
返回值:
函数的运行进程与当前shell是用一个进程,因此在函数体重不能使用exit退出函数体,这个关键字hi导致系统退出当前shell,因此函数有一个专用的返回命令return。在函数体中使用return,返回值在0~255之间,可以使用$?查看返回值。
局部变量local:
既然我们上面说函数的运行进程与当前shell是用一个进程,那么我们思考一个问题:如果我们在函数体中定义一个变量,而这个变量恰好与函数体外的变量同名,如果我们在函数体中对于该变量重新定义,那是不是会修改函数体外的变量?例如:
[root@localhost app]# name=xiaoli[root@localhost app]# echo $namexiaoli[root@localhost app]# fun1 () { name=haha;echo my name is $name; } [root@localhost app]# fun1 ------>调用func1函数my name is haha [root@localhost app]# echo $namehaha ------>变量已经被重新赋值
那么就提到了局部变量local的定义。使用local关键字可以定义函数体内的局部变量而不影响函数体外的全局变量:
[root@localhost app]# name=xiaoli[root@localhost app]# echo $namexiaoli[root@localhost app]# fun1 () { local name=haha;echo my name is $name; } ------>定义局部变量[root@localhost app]# fun1my name is haha[root@localhost app]# echo $namexiaoli ------>全局变量未被影响
查看函数:
declare -f 查看所有定义的函数
删除函数:
unset -f
练习:函数调用
[root@localhost app]# vim test.sh#!/bin/bashfunc_eg () { echo This is first arg}func_eg ------>调用func_eg 函数echo This is second arg[root@localhost app]# ./test.shThis is first arg ------>函数执行结果This is second arg ------>脚本执行结果
函数同样也接受位置参数:
[root@localhost app]# vim test.sh #!/bin/bashfunc_eg2 () { echo first arg is $1 echo second arg is $2 echo third arg is $3 echo All args are $*}func_eg2 35 87 18[root@localhost app]# ./test.shfirst arg is 35second arg is 87third arg is 18All args are 35 87 18
函数递归:函数可以实现直接或者间接的调用自身
最经典的例子就是汉诺塔的例子了,这里就不多实验了,有兴趣的同学欢迎百度~~
【数组】
变量是指存储单个元素的内存空间
数组是指存储多个元素的连续内存空间,相当于多个变量的集合。
在数组里的每个元素都有一个下标,即索引。数组中的索引分为普通索引和关联索引。
普通索引也称数组索引,即从编号0开始,递归增加。
关联索引是指不使用数组0开始的索引号,对索引采用自定义的格式,可以是abc单个字母,也可以是一个单词或者字符。
对于普通数组来说,如果索引号不连续,则称为数组的稀疏格式。
声明普通数组:declare -a (也可以不声明)
声明关联数组:declare -A (必须声明)
数组赋值:
一次赋值一个变量
array_name[1]=sunday
一次赋值全部变量
array_name=(1 2 3 4)
只赋值给特定变量
array_name=([0]="a" [3]="b")
调用数组:
${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素
[root@centos6 ~]# test=(a b c d) [root@centos6 ~]# echo ${test} ------> 省略下标号则默认引用下标为0的元素a[root@centos6 ~]# echo ${test[2]} ------>注意这里,因为我们的下标号是从0开始,所以下标为2的元素实际是第三个元素哦c[root@centos6 ~]# echo ${test[0]}a
引用数组所有元素:两种方法
${array_name[*]}
${array_name[@]}
显示数组中元素的个数:
${#array_name[*]}
${#array_name[@]}
[root@centos6 ~]# echo ${test[*]} a b c d[root@centos6 ~]# echo ${test[@]}a b c d[root@centos6 ~]# echo ${#test[*]}4[root@centos6 ~]# echo ${#test[@]}4
利用上面所说,我们可以对数组追加元素:
[root@centos6 ~]# test[${#test[*]}]=e ------>通过引用数组元素的个数实现向数组中追加元素[root@centos6 ~]# declare -a ------>查看定义的所有数组declare -a BASH_ARGC='()'declare -a BASH_ARGV='()'declare -a BASH_LINENO='()'declare -a BASH_SOURCE='()'declare -ar BASH_VERSINFO='([0]="4" [1]="1" [2]="2" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'declare -a DIRSTACK='()'declare -a FUNCNAME='()'declare -a GROUPS='()'declare -a PIPESTATUS='([0]="0")'declare -a test='([0]="a" [1]="b" [2]="c" [3]="d" [4]="e")' ------>定义成功
练习:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bashdeclare -a randdeclare -i max=0declare -i min=32767for i in {0..9}do rand[$i]=$RANDOM echo ${rand[$i]} if [ ${rand[$i]} -gt $max ] ;then let max=${rand[$i]} else let min=${rand[$i]} fidoneecho The max number is $maxecho The min number is $minunset i min max[root@centos6 app]# ./test.sh 1575426316314216902508713183206371952025223494The max number is 31421The min number is 23494
数组切片
格式:${ARRAY[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
[root@centos6 app]# echo ${test[*]}a b c d e[root@centos6 app]# echo ${test[*]:2:3} ------>跳过2个元素,取3个元素c d e
以上所说的语法就是shell流程控制执行的一些基本用法了,每个语法单独使用,或者混合使用都是可以的,可以相互嵌套来实现自己的目的,虽然有时候逻辑关系会很强,但只要梳理清楚了还是很容易理解的,希望自己好好加油吧~~~
- [shell]Shell流程控制
- shell流程控制学习
- Shell流程控制语句
- shell流程控制
- 流程控制Linux Shell
- linux shell 流程控制
- shell语法-流程控制
- shell流程控制语句
- shell-流程控制 if
- Shell 流程控制
- shell流程控制
- Shell流程控制
- shell--流程控制
- linux shell 流程控制
- Shell 流程控制
- shell 流程控制参数
- shell 流程控制
- Shell流程控制
- 1046. 划拳
- Unity简单地图循环长箭头实现
- 剑指Offer编程整理(六)
- kotlin集合
- Ubuntu 中文输入法,初试。
- shell 流程控制
- redis | string命令
- linux如何关闭防火墙
- Java面试题全集(下)
- 阿里云发布项目操作记录
- PAT甲级 1002. A+B for Polynomials (25)
- Angular(4)中加载Arcgis for JavaScript地图
- js中的focus()
- 解决客户端浏览器和服务器交互的编码问题详解