shell 流程控制

【前言】

  先看 shell脚本的基础入门,本文是shell编程的进阶。
  
计算1+2+…+100的值。
[root@localhost ~]# echo {1..100}|tr ' ' '+ '|bc   tr‘文件中原有的字符’ ‘要替换成的字符’  bc命令支持任意精度的计算
5050
【进入正题】
过程性的编程语言分为三类:顺序执行,选中执行,循环执行
顺序执行:在一个shell脚本中,按照你所写的命令一条一条 的按照顺序执行:
[root@localhost app]# vim test.sh
#!/bin/bash                   
echo i am $USER      
useradd Tom                           #创建用户
id Tom                                #显示用户信息
[root@localhost app]# ./test.sh       #执行脚本
i am root
uid=1021(Tom) gid=1021(Tom) groups=1021(Tom)

选择执行

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/bash
psd=`cat /dev/random |tr -dc '[0-9A-Za-z]'|head -c 8`  ------>将随机生成的8位密码存储到变量中
read -p "please input a username: " usr
id $usr &> /dev/null                                   ------>判断用户是否存在
if [ $? -eq 0 ];then                                   ------>如存在则显示用户已存在,不存在则执行else的分支代码
   echo $usr has exist
else
   useradd $usr 
   echo $psd | passwd --stdin $usr &>/dev/null
   echo $usr:$psd >> /app/passwdrandom                 ------>将用户名及密码输出到一个文件中
   chage -d 0 $usr                                     ------>强制用户下次登陆时修改密码  
   echo "user $usr is created!"
   id $usr
fi
unset psd usr                                          ------>删除变量
[root@localhost bin]# ./createuser.sh
please input a username: xiaowang
user 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: " ans
var1=`  echo $ans |tr -t '[a-z]' '[A-Z]'`    ------>将用户输入转换为大写字母,以防用户输入含有大写字母
case $var1 in
Y|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: no
you input no
[root@localhost bin]# ./yesorno.sh 
please input yes or no: yes
you input yes
[root@localhost bin]# ./yesorno.sh 
please input yes or no: haha
please input a right answer

  • 循环执行

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 $sum
sum is 5050                     #写入脚本也同样适用
[root@localhost bin]# sum=0;for ((i=1;i<=100;i++));do let sum+=i;done;echo sum is $sum
sum is 5050
  • for循环有多种写法,以上这种是数字循环,还有以下几种:
字符循环

#!/bin/bash
for i in `ls /app/`
do
    echo $i is file name
done

#!/bin/bash
list="aa bb cc dd"
for i in $list
do
    echo $i
done


路径循环
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
        echo
done
unset i j
[root@localhost bin]# ./for7_9x9.sh     
1*1=1
2*1=2   2*2=4
3*1=3   3*2=6   3*3=9
4*1=4   4*2=8   4*3=12  4*4=16
5*1=5   5*2=10  5*3=15  5*4=20  5*5=25
6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36
7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49
8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64
9*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    两种格式写法

练习:编写脚本,求100以内所有正奇数之和。
[root@localhost bin]# vim while1_jishusum.sh 
sum=0
i=1
while [ $i -le 100 ];do
        if [ $[$i%2] -eq 1 ];then
                let sum=sum+i
        fi
                let i++
done
echo 100以内所有正奇数之和是 $sum
unset i sum
[root@localhost bin]# ./while1_jishusum.sh 
100以内所有正奇数之和是 2500

练习:打印国际棋盘    思路:国际棋盘为八行八列,以两个空格为一个盘格,打印空格底色实现棋盘效果。
[root@localhost bin]# vim whileqipan.sh
#!/bin/bash
i=1
while ((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++
        echo
done
unset 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 米饭 炒菜 面条 馒头 退出
do
case $REPLY in
1|4)
        echo this price is 20
        ;;
2|3)
        echo this price is 12
        ;;
5)
        echo byebye
        exit 2
        ;;
*)
        echo no this number !
        echo $menu
esac
done
[root@localhost bin]# menu47.sh     
1) 米饭
2) 炒菜
3) 面条
4) 馒头
5) 退出
请选择菜单编号:1    
this price is 20
请选择菜单编号:4
this price is 20
请选择菜单编号:2
this price is 12
请选择菜单编号:5
byebye
[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 $name
xiaoli
[root@localhost app]# fun1 () { name=haha;echo my name is $name; }  
[root@localhost app]# fun1           ------>调用func1函数
my name is haha                      
[root@localhost app]# echo $name
haha                                 ------>变量已经被重新赋值


  •  那么就提到了局部变量local的定义。使用local关键字可以定义函数体内的局部变量而不影响函数体外的全局变量:
[root@localhost app]# name=xiaoli
[root@localhost app]# echo $name
xiaoli
[root@localhost app]# fun1 () { local name=haha;echo my name is $name; }                  ------>定义局部变量
[root@localhost app]# fun1
my name is haha
[root@localhost app]# echo $name
xiaoli                         ------>全局变量未被影响

查看函数:
declare -f 查看所有定义的函数
删除函数:
unset -f
练习:函数调用
[root@localhost app]# vim test.sh
#!/bin/bash
func_eg () {
        echo This is first arg
}

func_eg                   ------>调用func_eg 函数
echo This is second arg
[root@localhost app]# ./test.sh
This is first arg        ------>函数执行结果
This is second arg       ------>脚本执行结果

函数同样也接受位置参数:
[root@localhost app]# vim test.sh    
#!/bin/bash
func_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.sh
first arg is 35
second arg is 87
third arg is 18
All 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/bash
declare -a rand
declare -i max=0
declare -i min=32767
for 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]}
        fi
done
echo The max number is $max
echo The min number is $min
unset i min max
[root@centos6 app]# ./test.sh   
15754
26316
31421
6902
5087
13183
2063
7195
20252
23494
The max number is 31421
The 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流程控制执行的一些基本用法了,每个语法单独使用,或者混合使用都是可以的,可以相互嵌套来实现自己的目的,虽然有时候逻辑关系会很强,但只要梳理清楚了还是很容易理解的,希望自己好好加油吧~~~
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值