一、for 循环语句

1.for 语句的结构

for 变量名 in 取值列表

do

命令序列

done

注意:for循环语句需要有一个取值列表

备注:

for 语句的执行流程:首先将列表中的第一个取值赋给变量,并执行do…done 循环体中的命令序列;然后将列表中的第二个取值赋给变量,并执行循环体中的命令序列……依此类推,直到列表中的所有取值用完,最后将跳至done 语句,表示结束循环


2.for 语句应用示例

(1)根据姓名列表批量添加/删除用户

[root@localhost ~]# vim /root/users.txt

//用做测试的列表文件

chenye

dengchao

zhangjie

[root@localhost ~]# vim uaddfor.sh

//批量添加用户的脚本

#!/bin/bash

ULIST=$(cat /root/users.txt)

for UNAME in $ULIST

do

useradd $UNAME

echo "123456" | passwd --stdin $UNAME &>/dev/null

done

[root@localhost ~]# chmod +x uaddfor.sh

[root@localhost ~]# ./uaddfor.sh

//测试并确认执行结果

[root@localhost ~]# tail -3 /etc/passwd

chenye:x:1005:1005::/home/chenye:/bin/bash

dengchao:x:1006:1006::/home/dengchao:/bin/bash

zhangjie:x:1007:1007::/home/zhangjie:/bin/bash



[root@localhost ~]# vim udelfor.sh

//批量删除用户的脚本

#!/bin/bash

ULIST=$(cat /root/users.txt)

for UNAME in $ULIST

do

userdel -r $UNAME &>/dev/null

done

[root@localhost ~]# chmod +x udelfor.sh

[root@localhost ~]# ./udelfor.sh

//测试并确认执行结果

[root@localhost ~]# id chenye

id: chenye: no such user

//提示无此用户


(2)根据 IP 地址列表检查主机状态

[root@localhost ~]# vim /root/ipadds.txt

//用做测试的列表文件

172.16.16.1

172.16.16.22

172.16.16.220

[root@localhost ~]# vim chkhosts.sh

//循环检查各主机的脚本

#!/bin/bash

HLIST=$(cat /root/ipadds.txt)

for IP in $HLIST

do

ping -c 3 -i 0.2 -W 3 $IP &> /dev/null

//-c 发送包的数量;-i 发送 ping 包间隔;-W 超时时间

if [ $? -eq 0 ]

then

echo "Host $IP is up."

else

echo "Host $IP is down."

fi

done

[root@localhost ~]# chmod +x chkhosts.sh

[root@localhost ~]# ./chkhosts.sh

//测试并确认执行结果

Host 172.16.16.1 is up.

Host 172.16.16.22 is up.

Host 172.16.16.220 is down.


二、使用 while 循环语句

1.while 语句的结构

while 条件测试操作

do

命令序列

done

备注:

while 语句的执行流程:首先判断while 后的条件测试操作结果,如果条件成立,则执行do…done 循环体中的命令序列;返回while 后再次判断条件测试结果,如果条件仍然成立,则继续执行循环体;再次返回到while 后,判断条件测试结果……如此循环,直到while 后的条件测试结果不再成立为止,最后跳转到done 语句,表示结束循环。


2.while 语句应用示例

(1)批量添加/删除规律编号的用户

[root@localhost ~]# vim uaddwhile.sh

//批量添加用户的脚本

#!/bin/bash

PREFIX="stu"

i=1

while [ $i -le 20 ]

do

useradd ${PREFIX}$i

echo "123456" | passwd --stdin ${PREFIX}$i &> /dev/null

let i++

done

[root@localhost ~]# chmod +x uaddwhile.sh

[root@localhost ~]# ./uaddwhile.sh

[root@localhost ~]# grep "stu" /etc/passwd | tail -3

stu18:x:1022:1022::/home/stu18:/bin/bash

stu19:x:1023:1023::/home/stu19:/bin/bash

stu20:x:1024:1024::/home/stu20:/bin/bash



[root@localhost ~]# vim udelwhile.sh

//批量删除用户的脚本

#!/bin/bash

PREFIX="stu"

i=1

while [ $i -le 20 ]

do

userdel -r ${PREFIX}$i第 8 页 共 21 页

let i++

done

[root@localhost ~]# chmod +x udelwhile.sh

[root@localhost ~]# ./udelwhile.sh

//测试并确认执行结果

[root@localhost ~]# id stu20

id: stu20:无此用户

//提示无此用户


(2)猜价格游戏

[root@localhost ~]# vim pricegame.sh

#!/bin/bash

PRICE=$(expr $RANDOM % 1000)

TIMES=0

echo "商品实际价格范围为 0-999,猜猜看是多少?"

while true

do

read -p "请输入你猜测的价格数目:" INT

let TIMES++

if [ $INT -eq $PRICE ] ; then

echo "恭喜你答对了,实际价格是 $PRICE"

echo "你总共猜测了 $TIMES 次"

exit 0

elif [ $INT -gt $PRICE ] ; then

echo "太高了!"

else

echo "太低了!"fi

done

[root@localhost ~]# chmod +x pricegame.sh


[root@localhost ~]# ./pricegame.sh

商品实际价格范围为 0-999,猜猜看是多少?

请输入你猜测的价格数目:500

太高了!

请输入你猜测的价格数目:250

太低了!

请输入你猜测的价格数目:375

太高了!

请输入你猜测的价格数目:280

太高了!

请输入你猜测的价格数目:265

太高了!

请输入你猜测的价格数目:253

恭喜你答对了,实际价格是 253

你总共猜测了 6 次

注意:linux中随机数的取值范围是0--32767,和什么数取余,取余后的最大数就是谁,不包含该数字


三、until 循环语句

1.until 语句的结构

until 条件测试操作

do

命令序列

done

备注:until 循环与 while 循环类似,while 循环能实现的脚本 until 同样也可以实现,但区别是 while 循环在条件为真是继续执行循环,而 until 则是在条件为假时执行循环。


2.until 语句应用示例

[root@localhost ~]# vim until_v1.sh

#!/bin/bash

i=0;s=0

until [ $i -eq 50 ]

do

let "i=$i+1";let "s=$s+$i"

done

echo 'sum(1..50)='$s


[root@localhost ~]# chmod +x sum1to50_until_v1.sh

[root@localhost ~]# ./sum1to50_until_v1.sh

sum(1..50)=1275


四、Shell 函数

1.函数的用法

[function] 函数名() {

命令序列

[return x]

}

  • “function”关键字表示定义一个函数,可以省略;
  • “{”符号表示函数执行命令的入口,该符号可以与函数名同行也可以在函数名下一行 的句首;
  • “}”符号表示函数体结束,两个大括号之间{ }是函数体;
  • “命令序列”部分可以是任意的 Shell 命令,也可以调用其他函数;
  • “return”表示退出函数返回一个退出值,通过返回值判断执行是否成功,也可以使 用 exit 终止整个 Shell 脚本。

Shell 函数调用的方法为:函数名 [参数 1] [参数 2]。


2.案例  一个简单的函数脚本

[root@localhost ~]# cat exa1.sh

#!/bin/bash

zhangsan() {

echo "my name is zhangsan"

}


lisi() {

echo "my name is lisi"

}


zhangsan

lisi



[root@localhost ~]# sh exa1.sh


3.案例  带有参数的shell函数示例

[root@localhost ~]# cat exa2.sh

#!/bin/bash

name() {

echo "my name is $1"

}

name $1


[root@localhost ~]# sh exa2.sh zhangsan

my name is zhangsan

[root@localhost ~]# sh exa2.sh lisi

my name is lisi



4.案例 函数变量的作用范围

[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

备注:

通过内置命令local 将变量的值限定在函数内部。


5.案例 递归函数

Shell 也可以实现递归函数,就是可以调用自己本身的函数。

局部函数变量的一个特性是自成体系。除了从脚本命令行处获得的变量,自成体系的函数不需要使用任何外部资源。

这个特性使得函数可以递归地调用,也就是说,函数可以调用自己来得到结果。通常递归函数都有一个最终可以迭代到的基准值。许多高级数学算法用递归对复杂的方程进行逐级规约,直到基准值定义的那级。

[root@localhost ~]# vim fun_recursion.sh

#!/bin/bash

function factorial {

if [ $1 -eq 1 ]

then

echo 1

else

local temp=$[ $1 - 1 ]

local result=$(factorial $temp)

echo $[ $result * $1 ]

fi

}


read -p "Enter value: " value

result=$(factorial $value)

echo "The factorial of $value is: $result"


[root@localhost ~]# ./fun_recursion.sh


五、Shell 数组

Shell 中的数组与Java、C、Python 不同,只有一维数组,没有二维数组。

1.定义数组的方法

数组常用定义方法包括以下几种。

方法一:

数组名=(value0 value1 value2 ...)


方法二:

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


方法三:

列表名=”value0 value1 value2 ...”

数组名=($列表名)


方法四:

数组名[0]=”value”

数组名[1]=”value”

数组名[2]=”value”


2.获取数组长度

[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 ~]# vim aa

#!/bin/bash


echo "-- \$* 演示 ---"

for i in "$*"; do

echo $i

done


echo "-- \$@ 演示 ---"

for i in "$@"; do

echo $i

done


[root@localhost ~]# bash aa 1 2 3


3.读取某下标赋值

[root@localhost ~]# arr_index2=${arr_number[2]}

[root@localhost ~]# echo ${arr_number[2]}

//第三个元素

[root@localhost ~]# echo $arr_index2

3


echo ${aaa[3]}


4.数组遍历

[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


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

备注:

切片就是输出数组中指定连续的若干位,从第几位开始的往后几个数


6.数组替换

[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


7.数组删除

[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


六、Shell 脚本调试

除了 echo 命令之外,bash Shell 也有相应参数可以调试脚本。使用 bash 命令参数调 试,命令的语法为:

sh [-nvx] 脚本名

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

当脚本文件较长时,可以使用 set 命令指定调试一段脚本。

#!/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