shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)

文章目录

  • 一、循环语句
    • 1.1 for循环语句
      • 1.1.1 for语句结构
      • 1.1.2 for语句应用示例
      • 1.1.3 shell中let命令
    • 1.2 while 循环语句
      • 1.2.1 while语句结构
      • 1.2.2 while语句应用示例
      • continue和break
    • 1.3 until循环语句
      • 1.3.1 until语句的结构
      • 1.3.2 until语句应用示例
  • 二、shell函数
    • 2.1 shell函数概述
    • 2.1.1 函数应用示例
    • 2.2 函数的作用范围
    • 2.3 函数的参数
    • 2.4 递归函数
  • 三、shell数组
    • 3.1 shell数组概述
    • 3.2 shell数组定义方法
      • 方法一
      • 方法二
      • 方法三
      • 方法四
    • 3.3 数组包括的数据类型
      • 数值类型
      • 字符类型
    • 3.4 shell数组应用示例
    • 3.4 shell数组操作
      • 获取数组长度
      • 读取某下标赋值
      • 数组遍历
      • 数组切片
      • 数组替换
      • 数组删除
  • 四、shell脚本调试
    • echo命令
    • bash命令
    • set命令

一、循环语句

1.1 for循环语句

1.1.1 for语句结构

  • 读取不同的变量值,用来逐个执行同一组命令
语句结构
for 变量名 in 取值列表
do
	命令序列
done
语句结构举例
for 收件人 in 邮件地址列表
do
	发送邮件
done

shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)_第1张图片
for循环

  • 指定次数循环,
  • 遍历,把集合中的每个元素挨个读取一遍

1.1.2 for语句应用示例

示例1

  • 批量添加用户
    • 用户名存放在users.txt文件中,每行一个
    • 初始密码均设为19961207
    • 验证脚本
[root@localhost opt]# vim user.txt
zhangsan
lisi
wangermazi
zhaoliu
tianji
wangba
[root@localhost opt]# vim useradd.sh
#!/bin/bash
Lists=$(cat /opt/user.txt)
for bb in $Lists
do
        useradd $bb
        echo "19961207" | passwd --stdin $bb
        echo "$bb添加成功"
done
[root@localhost opt]# chmod +x useradd.sh 
[root@localhost opt]# ./useradd.sh 
Changing password for user zhangsan.
passwd: all authentication tokens updated successfully.
zhangsan添加成功
......
批量删除用户:在刚刚的基础上修改
[root@localhost opt]# vim useradd.sh
#!/bin/bash
Lists=$(cat /opt/user.txt)
for bb in $Lists
do
        userdel -r $bb	'-r 删除家目录'
        #echo "123456" | passwd --stdin $bb &> /dev/null
        echo "$bb删除成功"
done

[root@localhost opt]# ./useradd.sh 
zhangsan删除成功
lisi删除成功
wangermazi删除成功
zhaoliu删除成功
tianji删除成功
wangba删除成功
[root@localhost opt]# cat /etc/passwd | grep "bash"
root:x:0:0:root:/root:/bin/bash
dd:x:1000:1000:cn-tangzheng:/home/cn-tangzheng:/bin/bash

示例2

  • 根据主机IP地址检查主机状态
    • IP地址存放在ipadds.txt文件中,每行一个
    • 使用ping命令检测煮鸡蛋连通性
[root@localhost opt]# vim ipcheck.sh
#!/bin/bash
List=$(cat /opt/ipadds.txt)
for IP in $List
do
  ping -c 3 -i 0.2 -W 3 $IP > /dev/null
  if [ $? -eq 0 ];then
    echo "${IP} is up"
  else
    echo "${IP} is down"
  fi
done

[root@localhost opt]# sh ipcheck.sh
192.168.100.100 is down
192.168.1.112 is up
14.0.0.14 is up

示例三:

  • 输出1-10的数

法一:

#!/bin/bash
for ((i=1;i<=10;i++))
do
        echo "$i"
done

法二:

#!/bin/bash
i=1
for ((;i<=10;i++))
do
        echo "$i"
done

法三:

#!/bin/bash
i=1
for ((;i<=10;))
do
        echo "$i"
        let i++
done

法四:

#!/bin/bash
i=1
for ((;;))
do
  if [ $i -le 10 ]
        then
        echo "$i"
        let i++
  else
        exit 0
  fi
done

运行结果:

[root@localhost opt]# ./number.sh 
1
2
3
4
5
6
7
8
9
10

1.1.3 shell中let命令

  • let 对整数进行数学运算

  • let和双小括号 (( )) 一样,let 命令也只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。

  • 语法格式

let 表达式
或
let "表达式"
或
let '表达式'
以上方式都等价于 ((表达式))
  • 当表达式中含有 Shell 特殊字符(例如 |)时,需要用双引号" "或者单引号’ '将表达式包围起来。

  • 和 (( )) 类似,let 命令也支持一次性计算多个表达式,并且以最后一个表达式的值作为整个 let 命令的执行结果

1.2 while 循环语句

1.2.1 while语句结构

  • 重复测试某个条件,只要条件成立则反复执行
语句结构
while 条件测试操作
do
	命令序列
done
语句结构示例
while 未猜中正确的价格
do
	反复猜测商品价格
done

流程图:
shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)_第2张图片

1.2.2 while语句应用示例

示例1

  • 1-100不能被三整除的数
[root@localhost opt]# cat bunengchu3.sh 
#!/bin/bash

i=1
while [ $i -lt 100 ] 
do
  if [ `expr $i % 3` -ne 0 ];then
    echo $i
  fi
  let i++
done
[root@localhost opt]# sh bunengchu3.sh 
1
2
4
5
7
8
10
11
13
14
16
17
19
20
22
23
25
26
28
29
31
32
34
35
37
38
40
41
43
44
46
47
49
50
52
53
55
56
58
59
61
62
64
65
67
68
70
71
73
74
76
77
79
80
82
83
85
86
88
89
91
92
94
95
97
98

示例2

  • while死循环
    while true:死循环有时候也有奇效,可与用户交互
#!/bin/bash
while true
do
  read -p "请输入yes退出:" KEY
  if [ $KEY = yes ]
        then
          break
  fi
done
echo "正常退出"
[root@localhost opt]# ./sixunhuan.sh 
请输入yes退出:no
请输入yes退出:1
请输入yes退出:d
请输入yes退出:!
请输入yes退出:a
请输入yes退出:yes
正常退出

示例3

  • 猜商品价格游戏
    • 通过变量RANDOM获得随机数

    • 提示用户猜测并记录次数,猜中后退出循环

#!/bin/bash
A=`expr $RANDOM % 1000`
i=0
echo "商品的实际价格为0-999之间,猜猜看是多少?"
read -p "请输入你猜测的价格数目:" num
while [ $num -le 999 ] && [ $num -ge 1 ] 
do
        let i++
     if [ $num -eq $A ]
        then
        echo "恭喜你答对了,实际价格是$A"
        echo "你一共猜测了$i 次"
        exit 0
     elif [ $num -lt $A ]
        then
        echo "太低了"
        read -p "请输入你猜测的价格数目:" num
     else
        echo "太高了"
        read -p "请输入你猜测的价格数目:" num
     fi
  done

continue和break

continue

命令格式
continue n

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

流程图:
shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)_第3张图片
break

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

流程图:
shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)_第4张图片

  • break和continue的区别
    break 用来结束所有循环,循环语句不再有执行的机会;continue 用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。

1.3 until循环语句

1.3.1 until语句的结构

  • 重复测试某个条件,只要条件不成立则反复执行
until 条件测试操作
do
 命令序列
done
while 未超过10
do
 数字依次增加
done

流程图:
shell编程之循环语句与函数(for、while、until循环、shell函数与数组的使用)_第5张图片

1.3.2 until语句应用示例

  • 计算1–50的和

  • 通过循环累加的方式计算1–50的和

#!/bin/bash
i=1
sum=0
until [ $i -eq 51 ]
do
  echo "$i"
  let sum+=$i
  let i++
done
echo "和:$sum"
[root@localhost opt]# ./jisuan.sh 
1275

示例2

  • 为指定用户发送在线消息

    • 若指定用户不在线(未登陆系统),则每5S试一次,直至用户登录系统后再发送信息
  • 用户名与消息通过为止参数传递给脚本

条件:
1、位置参数个数大于1
2、姓名属于系统用户 /etc/passwd
3、用户是否在线 until who
4、发送消息 echo ”消息“ | write 用户名

[root@localhost opt]# vim usertalk.sh
#!/bin/bash
username=$1
#格式参数不能为空
if [ $# -lt 1 ];then
  echo "Usage:`basename $0`  []"
  exit 1  #异常退出
fi
#验证是否属于系统用户
if grep "^$username" /etc/passwd > /dev/null;then :  
#后面加冒号就可以不用>接着写了
else
  echo "no user"
  exit  2
fi
#测试用户是否在线,如果不在线,每5s联系一次
until who | grep "$username" > /dev/null;do
  echo "user not login"
  sleep 5
done
#发送消息
echo "$2" | write "$username"
echo "${username}发送成功"

二、shell函数

2.1 shell函数概述

  • 将命令序列按格式写在一起
  • 可方便重复使用命令序列
  • Shell函数定义
[function] fun_name () {
命令序列
[retrun n]    ########返回的是状态码
[echo n]    ######返回的是值
}
fun_name    #########调用函数
  • 其中,return返回的是状态码,需要使用$?调取

    • echo 返回的是值,使用变量调用

    • 传参:指位置变量

    • 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。

    • 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255

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

2.1.1 函数应用示例

示例1

  • 两个数字求和

    • 通过sum(){}定义函数
    • 使用read命令交互输入两个数并求和

[root@localhost opt]# vim hanshu1.sh
#!/bin/bash
function sum(){
  read -p "请输入加数:" num1
  read -p "请输入被加数:" num2
  sum=$(expr $num1 + $num2)
  [return abc]   #####中括号表示可有可无
  [echo $sum]
}
sum
#函数返回值在调用该函数后通过 $? 来获得
echo "返回值是:$?"
echo "和是:$sum"

[root@localhost opt]# sh hanshu1.sh 
请输入加数:45
请输入被加数:54
hanshu1.sh: 第 6 :return: abc: 需要数字参数
返回值是:255  ####说明返回值是错的
和是:99
由于shell状态码最大是255,所以当返回值大于255时会出错

示例2

  • 全局声明函数
    • 如果你希望直接从终端调用函数,可以将函数定义在主目录下的 .profile 文件,这样每次登录后,在命令提示符后面输入函数名字就可以立即调用
[root@localhost ~]# vim .bashrc (局部声明)
[root@localhost ~]# source .bashrc (更新)
[root@localhost ~]# vim /etc/profile
pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}
[root@localhost ~]# source /etc/profile

两个环境变量:
.bashrc 用户环境变量文件
/etc/profile 系统环境变量

2.2 函数的作用范围

  • 函数在shell脚本中仅在当前shell环境中有效
  • shell脚本中变量默认全局有效
  • 将变量限定在函数内部使用local命令
  • 示例
[root@localhost ~]# vim fun_scope.sh
myfun ()
{
local i i=8 echo $i
}
i=9 myfun echo $i
[root@localhost ~]# chmod +x fun_scope.sh
[root@localhost ~]# ./fun_scope.sh
8
9
  • 上述脚本中,myfun 函数内部使用了 local 命令设置变量 i,其作用是将变量 i 限定在函数内部。myfun 函数外部同样定义了变量 i,内部变量 i 和全局变量 i 互不影响。脚本执行时先调用了函数 myfun,函数内部变量 i 为 8,所以输出结果是 8。调用完函数之后,给变量i 赋值为 9,再打印外部变量 i,所以又输出 9。

2.3 函数的参数

参数的用法

函数名称 参数 1 参数 2 参数 3 ......

参数的表示方法
$1、$2、$3…… ${10} ${11}

2.4 递归函数

  • 调用自己本身的函数

  • 示例

    • 递归遍历目录,读取/var/log目录及其子目录
    • 通过定义递归函数list_files来实现
[root@localhost opt]# vim digui.sh

#!/bin/bash
function list_files(){
  for f in `ls $1`
  do
    #判断是否为目录
    if [ -d "$1/$f" ]
    then
      echo "$2$f"
      #递归调用
      list_files "$1/$f" "  $2"      ##递归自己调自己,又变成$1了
    else
      echo "$2$f"
    fi
  done
}
list_files "/var/log" ""

[root@localhost opt]# sh digui.sh 
anaconda
  anaconda.log
  ifcfg.log
  journal.log
  ks-script-MdTpNR.log
  packaging.log
  program.log
  storage.log
  syslog
  X.log
audit
  audit.log
boot.log
boot.log-20200716
boot.log-20200719
boot.log-20200720
btmp
chrony
cron
cron-20200719
cups
  access_log
  access_log-20200719
  error_log
  page_log
dmesg
dmesg.old
firewalld
gdm
  :0-greeter.log
  :0-greeter.log.1
  :0-greeter.log.2
  :0-greeter.log.3
  :0-greeter.log.4
  :0.log
  :0.log.1
  :0.log.2
  :0.log.3
  :0.log.4
  :1-greeter.log
  :1.log
glusterfs
grubby_prune_debug
lastlog
libvirt
  qemu
maillog
maillog-20200719
messages
messages-20200719
ntpstats
pluto
  peer
ppp
qemu-ga
rhsm
sa
  sa15
  sa16
  sa17
  sa18
  sa19
  sa20
  sar16
  sar17
  sar18
  sar19
samba
  old
secure
secure-20200719
speech-dispatcher
spooler
spooler-20200719
sssd
swtpm
  libvirt
    qemu
tallylog
tuned
  tuned.log
vmware-network.1.log
vmware-network.2.log
vmware-network.3.log
vmware-network.4.log
vmware-network.5.log
vmware-network.6.log
vmware-network.log
vmware-vgauthsvc.log.0
vmware-vmsvc.log
vmware-vmusr.log
wpa_supplicant.log
wtmp
xferlog
Xorg.0.log
Xorg.0.log.old
Xorg.1.log
Xorg.9.log
yum.log

三、shell数组

3.1 shell数组概述

  • 在 Shell 脚本中,数组是一种常见的数据结构,主要的应用场景包括:获取数组长度、获取元素长度、遍历元素、元素切片、元素替换、元素删除等等
  • Shell 中的数组与 Java、C、Python 不同,只有一维数组,没有二维数组。
  • 数组元素的大小与限制,也不需要事先定义。Shell 数组用括号()来表示,元素用空格分隔,元素的下标与大部分编程语言类似从 0 开始。

3.2 shell数组定义方法

方法一

数组名=(value0 value1 value2 …)

[root@localhost opt]# num=(11 22 33 44)
[root@localhost opt]# echo $num     ###这样调不出来
11
[root@localhost opt]# echo ${num[*]}       ###*代表全部,[]代表元素的下标
11 22 33 44
[root@localhost opt]# echo ${num[2]}          
33

方法二

数组名=([0]=value [1]=value [2]=value …)

[root@localhost opt]# num=([0]=55 [1]=66 [2]=77 [3]=88)
[root@localhost opt]# echo ${num[@]}          ########@和*都表示所有
55 66 77 88
[root@localhost opt]# echo ${num[*]}
55 66 77 88

方法三

列表名=”value0 value1 value2 …”
数组名=($列表名)

[root@localhost opt]# list="11 12 13 14"      ####先定义列表,列表再赋值给数组
[root@localhost opt]# num=($list)
[root@localhost opt]# echo ${num[*]}
11 12 13 14

方法四

数组名[0]=”value” 数组名[1]=”value” 数组名[2]=”value”

方法四一般用来作替换,替换掉某一个元素
[root@localhost opt]# echo ${num[*]}
11 12 13 14
[root@localhost opt]# num[1]=88
[root@localhost opt]# echo ${num[*]}
11 88 13 14

3.3 数组包括的数据类型

数值类型

字符类型

  • 使用**“”’’**定义(双引号或单引号)
如:[11,22,33,44]    在内存中开辟了连续的空间
  配合着循环使用

数组名称:arr                                    arr=(11,22,33,44)      索引
数组元素:如11                                             0   1   2   3
数组长度 :4 (元素的个数)
数组下标:33元素的下标值是2

3.4 shell数组应用示例

需求:创建一个1-100的数组

[root@localhost opt]# vim arr.sh
#!/bin/bash
for ((i=0;i<=99;i++));do
  arr[$i]=$[$i+1]
done
echo ${arr[*]}

创建存放(1-100)奇数的数组:
分析:

下标     元素        增量
0         1           1
1         3           2
2         5           3
3         7           4
$i        $k          $j
k=$i+$j
[root@localhost opt]# vim arr02.sh
#!/bin/bash
#创建存放(1-100)奇数的数组
k=0  #元素
j=1  #增量
for ((i=0;i<=99;i++))
do
  k=$[$i+$j]
  let j++
  #最大元素不能超过100
  if [ $k -le 100 ]
  then
    arr[$i]=$k
  fi
done
echo ${arr[*]}
[root@localhost opt]# sh arr02.sh
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99

创建任意数字及长度的数组,根据客户需求加入元素:
分析:

利用好死循环和break

while 条件始终成立
do
  if 判断成立
    break
done
[root@localhost opt]# vim arr03.sh

#!/bin/bash
#创建任意数字及长度的数组,根据客户需求
i=0 #数组下标
while true
do
  read -p "是否存入元素(yes/no):" doing
  if [ $doing == "no" ];then
    break
  fi
  read -p "请输入第$[$i+1]个元素:" key
  arr[$i]=$key
  let i++
done
echo ${arr[*]}
echo ${#arr[@]}    ##数组长度

[root@localhost opt]# sh arr03.sh
是否存入元素(yes/no):yes
请输入第1个元素:33
是否存入元素(yes/no):yes
请输入第2个元素:66
是否存入元素(yes/no):yes
请输入第3个元素:99
是否存入元素(yes/no):no
33 66 99

3.4 shell数组操作

获取数组长度

[root@localhost ~]# arr_number=(1 2 3 4 5) 
[root@localhost ~]# arr_length=${#arr_number[*]} 
[root@localhost ~]# echo $arr_length
5
[root@localhost ~]# arr_length_1=${#arr_number[@]}
[root@localhost ~]# echo $arr_length_1
5

读取某下标赋值

[root@localhost ~]# arr_index2=${arr_number[2]}	//第三个元素
[root@localhost ~]# echo $arr_index2
3

数组遍历

[root@localhost ~]# vim array_traverse.sh
#!/bin/bash 
arr_number=(1 2 3 4 5) 
for v in ${arr_number[@]} 
do
  echo $v 
done

[root@localhost ~]# chmod +x array_traverse.sh
[root@localhost ~]# ./array_traverse.sh
1
2
3
4
5

数组切片

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# echo ${arr[@]}	//输出整个数组
1 2 3 4 5
[root@centos-7 ~]# echo ${arr[@]:0:2}	//${数组名[@或*]:起始位置:长度} 
1 2
[root@centos-7 ~]# echo ${arr[@]:2:3}
3 4 5

将数组切片之后,返回的是字符串,以空格作为分隔符。

数组替换

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# echo ${arr[@]/4/66}	//${数组名[@或*]/查找字符/替换字符} 
1 2 3 66 5
[root@centos-7 ~]# echo ${arr[@]}	//并不会替换数组原有内容
1 2 3 4 5
[root@centos-7 ~]# arr=(${arr[@]/4/66})	//要实现改变原有数组,可通过重新赋值实现
[root@centos-7 ~]# echo ${arr[@]}
1 2 3 66 5

只是临时的更改输出,把原来的一份复制到缓冲区中,原来的没变
永久修改就把原来内容的覆盖掉
num=(${num[*]/44/88})

数组删除

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr	//删除数组
[root@centos-7 ~]# echo ${arr[*]}

[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr[2]	//删除第三个元素
[root@centos-7 ~]# echo ${arr[*]}
1 2 4 5

示例
删除低于数组中低于60分的成绩:

[root@localhost opt]# vim chengji04.sh

#!/bin/bash
score=(77 88 55 33 99 44)
i=0
for v in ${score[@]};do
  if [ ${score[$i]} -lt 60 ];then
    unset score[$i]
  fi
  let i++
done
echo ${score[@]} 
[root@localhost opt]# sh chengji04.sh
77 88 99

四、shell脚本调试

echo命令

echo 命令是最有用的调试脚本工具之一,一般在可能出现问题的脚本中加入 echo 命令,采用的是分段排查的方式

bash命令

命令的语法为

sh [-nvx] 脚本名

常用参数的具体含义为:

  • -n:不会执行该脚本,仅查询脚本语法是否有问题,如果没有语法问题就不显示任 何内容,如果有问题会提示报错。
  • -v:在执行脚本时,先将脚本的内容输出到屏幕上然后执行脚本,如果有错误,也 会给出错误提示。
  • -x:将执行的脚本内容输出到屏幕上,这个是对调试很有用的参数。

set命令

set -x	//开启调试模式
set +x	//关闭调试模式
#!/bin/bash
set -x	//开启调试模式
read -p "请输入您的分数(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]  
then
echo "$GRADE 分!优秀"
set +x	//关闭调试模式
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ]  
then
  echo "$GRADE 分,合格"
else 
  echo "$GRADE 分?不合格"
fi

你可能感兴趣的:(shell脚本,shell,linux,经验分享,字符串)