shell脚本--for、while循环、until语句、shell函数、数组

文章目录

  • for循环
    • 遍历
      • 例:ping主机
      • 例:创建批量用户
    • 指定循环次数
      • 例:生成1-5
      • 例:计算5以内偶数和
  • while循环
      • 例:批量添加用户,用户名称以ter开头,如ter1、ter2...ter5,并设置密码123456
      • 例:输出10以内不能被3整除的数
  • 双重循环
      • 列:九九乘法口诀表
      • 列:输出等腰三角形
  • until语句
    • 例:求1-50和
    • 例:与系统中用户通信
  • shell函数
    • 例:求两数和
    • 传参
    • 编写登录系统后便可使用的自定义函数
  • 数组
    • 数组概念
    • 定义数组
      • 例:生成1-100的数组
      • 例:生成1-100数组
      • 例:生成1-10奇数数组
      • 例:创建任意长度任意元素的数组,根据需求加元素
    • 数组操作
      • 例:把成绩不足六十的加到60
      • 例:取最大成绩
      • 例:排序

for循环

读取不同的变量值,用来逐个执行同一组命令。for循环可实现遍历,即把集合中的元素逐个取一遍,执行命令;也可实现指定次数循环。

  • 语句结构
    for 变量名 in 取值列表
    do
    命令序列
    done
  • 语句结构举例
    for 收件人 in 邮件地址列表
    do
    发送邮件
    done
    逻辑结构
    shell脚本--for、while循环、until语句、shell函数、数组_第1张图片

遍历

例:ping主机

[root@localhost ~]# vim pinghost01.sh 

#!/bin/bash
#
List=$(cat /opt/hostname)
for host in $List
do
 ping -c 3 -i 0.3 -W 3 $host &> /dev/null
 if [ $? -eq 0 ]
  then
     echo "host $host up"
 else
     echo "host $host down"
 fi
done
[root@localhost ~]# cat /opt/hostname   ###需要创建文件,让脚本去取
20.0.0.1
20.0.0.2
20.0.0.111
[root@localhost ~]# bash pinghost01.sh 
host 20.0.0.1 up
host 20.0.0.2 up
host 20.0.0.111 down
[root@localhost ~]# 

例:创建批量用户

[root@localhost ~]# vim /opt/username   ##用户名存放在这里
lisa1
lisa2

[root@localhost ~]# vim usradd.sh 
#!/bin/bash
#
List=$(cat /opt/username)
for username in $List
do
 useradd $username
 echo "123456" |passwd --stdin $username
done
[root@localhost ~]# bash usradd.sh 
更改用户 lisa1 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 lisa2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@localhost ~]# tail -5 /etc/passwd
...
lisa1:x:1024:1024::/home/lisa1:/bin/bash
lisa2:x:1025:1025::/home/lisa2:/bin/bash

指定循环次数

例:生成1-5

[root@localhost ~]# cat evil01.sh
#!/bin/bash
#
for ((i=1;i <= 5;i++))
do
 echo $i
done

[root@localhost ~]# cat evil02.sh 
#!/bin/bash
#
i=1
for ((;i <= 5;i++))
do 
 echo $i
done 

[root@localhost ~]# cat evil03.sh 
#!/bin/bash
#
for ((i=1;i <= 5;))
do 
 echo $i
 let i++
done

[root@localhost ~]# cat evil04.sh 
#!/bin/bash
#
for ((i=1;i <= 5;))
do 
 echo $i
 i=$[$i + 1]
done

[root@localhost ~]# cat evil05.sh 
#!/bin/bash
#
for ((i=1;i <= 5;))
do
 echo $i
 i=`expr $i + 1`
done

[root@localhost ~]# cat evil06.sh 
#!/bin/bash
#
i=1
for ((;;))
do
 if [ $i -le 5 ]
  then
   echo $i
   let i++
 else
   exit 0
 fi
done
[root@localhost ~]# 
结果均为
[root@localhost ~]# bash evil06.sh 
1
2
3
4
5

例:计算5以内偶数和

方法1:
[root@localhost ~]# vim echo01.sh 

#!/bin/bash
#
sum=0
for ((i=1;i <= 5;i++))
do
 if [ $[$i%2] -eq 0 ]      ##[` expr $i %2` -eq 0]
  then
    sum=$[$i+$sum]      ##sum=` expr $i + $sum`
 fi
done
echo $sum
方法2:
[root@localhost ~]# cat echo01.sh 
#!/bin/bash
#
sum=0
for ((i=1;i <= 5;i++))
do
 if [ $[$i%2] -eq 0 ]
   then
     let sum=$sum+$i
 fi
done
echo $sum

[root@localhost ~]# bash echo01.sh 
6

while循环

重复测试某个条件,只要条件成立则反复执行。

  • 语句结构
    while 条件测试操作
    do
    命令序列
    done
  • 语句结构示例
    while 未猜中正确的价格
    do
    反复猜测商品价格
    done
  • 逻辑结构
    shell脚本--for、while循环、until语句、shell函数、数组_第2张图片

例:批量添加用户,用户名称以ter开头,如ter1、ter2…ter5,并设置密码123456

[root@localhost ~]# vim echo02.sh 
#!/bin/bash
#
i=1
t="ter"
while [ $i -le 5 ]
do
  name=$t$i
  useradd $name
  echo "123456" |passwd --stdin $name
  let i++
done
[root@localhost ~]# 
[root@localhost ~]# bash echo02.sh 
useradd:用户“ter1”已存在
更改用户 ter1 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 ter2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 ter3 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 ter4 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 ter5 的密码 。
passwd:所有的身份验证令牌已经成功更新。

例:输出10以内不能被3整除的数

[root@localhost ~]# vim echo03.sh 

#!/bin/bash
#
i=10
while [ $i -ge 0 ]
do
 if [ `expr $i % 3` -eq 0 ]
   then
     let i--
     continue
   else
     echo $i
     let i--
 fi
done
[root@localhost ~]# bash echo03.sh 
10
8
7
5
4
2
1
[root@localhost ~]# vim while02.sh 

#!/bin/bash
#
i=1
while [ $i -le 10 ]
do
 if [ `expr $i % 3` -ne 0 ]
   then
      echo "$i"
      let i++
 else
      let i++
 fi
done
[root@localhost ~]# bash while02.sh 
1
2
4
5
7
8
10

双重循环

在双重循环中,外重循环控制行,内重循环控制列,当外重循环拿出一个元素来执行一次时,内重循环需要所有元素都执行一遍。

列:九九乘法口诀表

方法一:两个for循环实现了输出1x[1-9]…9[1-9]的功能,显然有很多输出的是我们不要的,-n可以不换行使结果横着打印,观察乘法口诀表第一行输出1x1第二行输出2x1、2x2,i大于J时我们输出,使用if条件语句将不要的过滤,不符合条件时的break打断当前循环不输出并使用echo “”实现换行,到一次循环就在下一行了

[root@localhost ~]# vim w.sh
#!/bin/bash
#
for ((i=1;i<=9;i++))
do
 for ((j=1;j<=9;j++))
 do
   if [ $i -ge $j ];then
    name="$j x $i"       ##也可以使用"$i x $j"可能更助于理解,在后面为了美观才调的
    echo -ne "${name}=`expr $i \* $j`  "   ##这行最后有空格,列之间的空格
   else
    echo ""
    break
   fi
 done
done
[root@localhost ~]# bash w.sh 
1 x 1=1  
1 x 2=2  2 x 2=4  
1 x 3=3  2 x 3=6  3 x 3=9  
1 x 4=4  2 x 4=8  3 x 4=12  4 x 4=16  
1 x 5=5  2 x 5=10  3 x 5=15  4 x 5=20  5 x 5=25  
1 x 6=6  2 x 6=12  3 x 6=18  4 x 6=24  5 x 6=30  6 x 6=36  
1 x 7=7  2 x 7=14  3 x 7=21  4 x 7=28  5 x 7=35  6 x 7=42  7 x 7=49  
1 x 8=8  2 x 8=16  3 x 8=24  4 x 8=32  5 x 8=40  6 x 8=48  7 x 8=56  8 x 8=64  
1 x 9=9  2 x 9=18  3 x 9=27  4 x 9=36  5 x 9=45  6 x 9=54  7 x 9=63  8 x 9=72  9 x 9=81 

方法二:
核心在于j<=i,直接输出的想要的,echo -ne 中-n是表示不换行,-e是表示可识别转义符如\t

[root@localhost ~]# vim w1.sh 

#!/bin/bash
#
for ((i=1;i<=9;i++))
do
 for ((j=1;j<=i;j++))
 do
   name="$j x $i"  ##对应1 x 2中1为j,2为i,观察规律,在j<=i时输出
   echo -ne "$name=`expr $j \* $i`  "
 done
 echo ""     
done
[root@localhost ~]# bash w1.sh 
1 x 1=1 
1 x 2=2 2 x 2=4 
1 x 3=3 2 x 3=6 3 x 3=9 
1 x 4=4 2 x 4=8 3 x 4=12 4 x 4=16 
1 x 5=5 2 x 5=10 3 x 5=15 4 x 5=20 5 x 5=25 
1 x 6=6 2 x 6=12 3 x 6=18 4 x 6=24 5 x 6=30 6 x 6=36 
1 x 7=7 2 x 7=14 3 x 7=21 4 x 7=28 5 x 7=35 6 x 7=42 7 x 7=49 
1 x 8=8 2 x 8=16 3 x 8=24 4 x 8=32 5 x 8=40 6 x 8=48 7 x 8=56 8 x 8=64 
1 x 9=9 2 x 9=18 3 x 9=27 4 x 9=36 5 x 9=45 6 x 9=54 7 x 9=63 8 x 9=72 9 x 9=81 

列:输出等腰三角形

[root@localhost ~]# vim techer03.sh 

#!/bin/bash
for ((i=1;i<=5;i++))
do
 for ((j=5;j>=i;j--))
 do
  echo -n " "
 done
 for ((k=1;k<=2*i-1;k++))
 do
  echo -n "*"
 done
 echo ""
done
[root@localhost ~]# bash techer03.sh 
     *
    ***
   *****
  *******
 *********

其实内层的两个for语句相互较独立,但是第一个for循环为第二个for循环做了基础,使第二个for循环出现图形的起点向后移动,让我更清晰的看到shell脚本是由一系列按顺序执行命令的集合

until语句

重复测试某个条件,只要条件不成立则反复执行

  • until 条件测试操作
    do
    命令序列
    done
  • while 未超过10
    do
    数字依次增加
    done
  • 逻辑结构
    shell脚本--for、while循环、until语句、shell函数、数组_第3张图片

例:求1-50和

[root@localhost ~]# vim until03.sh 

#!/bin/bash
#
i=1
sum=0
until [ $i -eq 51 ]
do
 let sum+=$i;let i++
done
echo $sum
[root@localhost ~]# bash until03.sh 
1275

例:与系统中用户通信

需求:
为指定用户发送在线消息
若指定用户不在线(未登陆系统),则每3s试一次,直至用户登录系统后再发送信息
用户名与消息通过位置参数传递给脚本
分析:

  • 用户是否在线可用who命令查看,系统是否有该用户可查看/etc/passwd/
  • 关于用户名和消息两个位置参数可有数量$#设置限制,当没有用户名时提示用户
  • 发送消息使用语句:echo “消息内容” | write 用户,如:echo “hello” |write tom
[root@localhost ~]# vim until04.sh 

#!/bin/bash
#
username=$1
#执行脚本时格式不规范,显示提醒
if [ $# -lt 1 ];then
 echo "请在${0}后输入,用户名和发送的消息"
 exit 1
fi
#查看输入的用户是否为系统中的用户
if grep "^$username" /etc/passwd > /dev/null;then :
else
 echo "The user not exist"
 exit 2
fi
#查看用户是否在线,不在线则每隔3秒查询一次,在线则跳到下个语句
until who | grep "$username" > /dev/null;do
 echo "The user not login"
 sleep 3
done
#在线时,发送消息
 echo "$2" | write "$username"
 echo "${username}发送成功"
[root@localhost ~]# bash until04.sh ter5 h   ##用户不在线,后登录
The user not login
The user not login
The user not login
ter5发送成功    
[root@localhost ~]# bash until04.sh 
请在until04.sh后输入,用户名和发送的消息

shell函数

shell函数将命令序列按格式写在一起,可以方便重复使用命令序列

  • 格式
    [function] 函数名(){
    命令序列
    [return x]
    }
    []内的均为选填,return是函数执行完之后产生的状态码,可用返回给系统,可用$?调取
    可用echo来返回函数执行的结果
    使用位置变量可以实现传参
  • 调用函数的方法
    函数名 [参数1] [参数2]
    在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数… ${10}

例:求两数和

[root@localhost ~]# vim hansu01.sh 
#!/bin/bash
#
function sum(){
 read -p "请输入第一个整数:" num1
 read -p "请输入第二个整数:" num2
 SUM=$[$num1+$num2]
 echo "和为:$SUM"
}
sum           ##执行脚本的时候有这条函数名,系统才知道要执行哪个函数,就像一个命令一样
[root@localhost ~]# bash hansu01.sh 
请输入第一个整数:1
请输入第二个整数:2
和为:3
[root@localhost ~]# vim hansu01.sh 

#!/bin/bash
#
function sum(){
 read -p "请输入第一个整数:" num1
 read -p "请输入第二个整数:" num2
 SUM=$[$num1+$num2]
 echo "和为:$SUM"
 return 100
}
number=`sum`
echo $?                 ###输出状态码
echo $number
[root@localhost ~]# bash hansu01.sh 
请输入第一个整数:1
请输入第二个整数:2
100
和为:3

传参

[root@localhost ~]# vim hansu01.sh 

#!/bin/bash
#
function sum(){
 #read -p "请输入第一个整数:" num1
 #read -p "请输入第二个整数:" num2
 SUM=$[$1+$2]
 echo "和为:$SUM"
 return 100
}
sum 1 2
[root@localhost ~]# bash hansu01.sh 
和为:3

编写登录系统后便可使用的自定义函数

将函数脚本加到~/.bashrc下,在source ~./bashrc
例:
自己定义了一个函数sum

[root@localhost ~]# vim hansu01.sh 

#!/bin/bash
#
function sum(){
  read -p "请输入第一个整数:" num1
  read -p "请输入第二个整数:" num2
  SUM=$[$num1+num2]
  echo "$SUM"
}
sum

加到家目录下的.bashrc文件里,即可在当前bash环境下直接调用

[root@localhost ~]# vim ~/.bashrc

# .bashrc

# User specific aliases and functions

alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi
#!/bin/bash                   ###这里为我们自己定义的函数
#
function sum(){
  read -p "请输入第一个整数:" num1
  read -p "请输入第二个整数:" num2
  SUM=$[$num1+num2]
  echo "$SUM"
}
                           ##最后不用加调用函数的函数名了
[root@localhost ~]# source ~/.bashrc
[root@localhost ~]# sum
请输入第一个整数:1
请输入第二个整数:2
3

数组

数组概念

数组:相同类型数据的集合,如:arr=(11,22,33,44),数组是在内存中开辟了连续的空间,可配合循环使用。

  • 数组名称:如arr即为数组名称
  • 数组元素:11,22,33,44
  • 数组长度:数组中元素的个数4
  • 数组下标:索引从0开始,33元素的下标值为2
  • 借助for语句实现数组的遍历
    for 临时变量 in 数组
    do
    done

定义数组

  • 方法一:
    基本格式 数组名=(value0 value1 value2…)
[root@localhost ~]# num=(1 2 3)       ##定义数组
[root@localhost ~]# echo ${num[*]}    ##调用数组,*表示下标所有
1 2 3
[root@localhost ~]# echo ${num[0]}    ##调用下标为0,也就是第一个元素
1
[root@localhost ~]# 
  • 方法二:
    基本格式 数组名=([0]=value [1]=value [2]=value…)
    定义和调用数组
[root@localhost ~]# num=([0]=1 [1]=2 [2]=3 )  ##定义数组
[root@localhost ~]# echo ${num[@]}       ##调用数组,@与*差不多
1 2 3
[root@localhost ~]# echo ${num[1]}
2
[root@localhost ~]#
  • 方法三:用在什么情况下?
    基本格式 列表名=“value0 value1 value2”
    数组名=($列表名)
    先定义给列表,再赋予给数组
[root@localhost ~]# list="1 2 3"    ##先定义列表
[root@localhost ~]# num=($list)   ##再赋予给数组
[root@localhost ~]# echo ${num[*]}
1 2 3
[root@localhost ~]# 

拓展
调某个元素会有问题,可盘,看是否可以输出某个元素

变量是否可以看成是只有一个元素的数组
[root@localhost ~]# vim g.sh 

#!/bin/bash
#
num=`cat /opt/a.txt`   ##a.txt的内容为1 2 3
i=1
echo ${num[*]}
echo ${num[0]}
echo ${num[1]}
echo ${i[0]}
echo $num
[root@localhost ~]# bash g.sh 
1 2 3
1 2 3

1
1 2 3
[root@localhost ~]# bash g.sh 
1 2 3
2
[root@localhost ~]# vim g.
[root@localhost ~]# vim g.sh 

#!/bin/bash
#
list=`cat /opt/a.txt`
num=($list)
echo ${num[*]}
echo ${num[1]}

例:生成1-100的数组

[root@localhost ~]# vim until05.sh 

#!/bin/bash
#
i=1
until [ $i -eq 101 ]
do
  echo $i >> /opt/100.txt
  let i++
done
list=`cat /opt/100.txt`
num=($list)
echo ${num[*]}
[root@localhost ~]# bash until05.sh 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  • 方法四:
    基本格式 数组名[0]=“value”
    数组名[1]=“value”
    数组名[2]=“value” …

常用在替换某一个元素

[root@localhost ~]# num[0]=1  ##定义
[root@localhost ~]# nmu[1]=2
[root@localhost ~]# num[2]=2
[root@localhost ~]# num[3]=3
[root@localhost ~]# echo ${num[*]}
1 2 2 3
[root@localhost ~]# num[2]=3    ##替换
[root@localhost ~]# num[3]=4
[root@localhost ~]# echo ${num[*]}
1 2 3 4
[root@localhost ~]# 

例:生成1-100数组

用到第四种定义数组的方法
[root@localhost ~]# vim h.sh 

#!/bin/bash
#
for ((i=0;i<=99;i++))
do
  arr[$i]=$[$i+1]
done
echo ${arr[*]}
[root@localhost ~]# bash h.sh 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

例:生成1-10奇数数组

这是我的第一想法,非常不符合逻辑,这样输出虽生成了奇数,但arr[0]、arr[2]…都不存在了,跳跃了

[root@localhost ~]# vim shuzu01.sh 

#!/bin/bash
#
for ((i=1;i<=10;i++))
do
 if [ `expr $i % 2` -ne 0 ]
  then
    arr[$i]=$[$i]
 fi
done
echo ${arr[*]}
[root@localhost ~]# bash shuzu01.sh 
1 3 5 7 9

正确做法

[root@localhost ~]# vim jishu01.sh 

#!/bin/bash
#
#k=0
j=1
for ((i=0;i<=9;i++))
do
 k=$[$i+$j]
 let j++
 if [ $k -le 10 ];then
 arr[i]=$k
 fi
done
echo ${arr[*]}
[root@localhost ~]# bash jishu01.sh 
1 3 5 7 9

例:创建任意长度任意元素的数组,根据需求加元素

[root@localhost ~]# vim q.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[*]}
[root@localhost ~]# bash q.sh 
是否存入元素(yes/no):yes
请存入第1个元素:1
是否存入元素(yes/no):yes
请存入第2个元素:3
是否存入元素(yes/no):yes
请存入第3个元素:5
是否存入元素(yes/no):yes
请存入第4个元素:6
是否存入元素(yes/no):no
1 3 5 6

数组操作

  • 获取数组长度
    arr_length=${#arr[*]}
[root@localhost ~]# arr=(1 2 3 4)
[root@localhost ~]# arr_length=${#arr[*]}
[root@localhost ~]# echo $arr_length
4
[root@localhost ~]# 
  • 数值遍历
    使用for语句
[root@localhost ~]# vim r.sh 
#!/bin/bash
#
num=(1 2 3 4)
for v in ${num[*]}
do
 echo $v
done
[root@localhost ~]# bash r.sh 
1
2
3
4
  • 数组切片
    ${数组名[*]:起始位置:长度}
  • 数组t替换
    ${数组名[*]/查找字符/替换字符}
  • 数组删除
    unset 数组名
    unset 数组元素

例:把成绩不足六十的加到60

初始想法
shell脚本--for、while循环、until语句、shell函数、数组_第4张图片

改进

[root@localhost ~]# bash z.sh 
60 60 70 90
[root@localhost ~]# vim z.sh 

#!/bin/bash
#
score=(55 60 70 90)
for ((i=0;i<${#score[*]};i++))
do
 if [ ${score[$i]} -lt 60 ];then
   new[$i]=60
 else
   new[$i]=${score[$i]}
 fi
done
echo ${new[*]}

例:取最大成绩

[root@localhost ~]# vim m.sh 

#!/bin/bash
#
score=(55 60 45 90)
temp=0
for ((i=0;i<${#score[*]};i++))
do
  if [ ${score[$i]} -gt $temp ]
  then
    temp=${score[$i]}
  fi
done
echo $temp
[root@localhost ~]# bash m.sh 
90

例:排序

[root@localhost ~]# vim m.sh 
#!/bin/bash
score=(72 63 88 91 45)
for ((i=1;i<${#score[*];i++}))
do
 for ((j=1;j<${#score[*]}-i;j++))
 do
    if [ ${score[$j] -gt ${score[`expr $j + 1`]} ];then
       tmp=${score[`expr $j + 1`]
       score[`expr $j + 1`]=${score[$j]}
       score[$j]=$tmp
    fi
  done
done
echo ${score[*]}

你可能感兴趣的:(shell脚本--for、while循环、until语句、shell函数、数组)