第五章 流程控制之循环
for循环
-
for循环是最简单,也是最常用的循环语句。与其他的程序设计语言一样,for循环都是初学者在学习循环结构时的入门课程。for循环通常用于遍历整个对象或者数字列表。按照循环条件的不同,for循环语句可以分为带列表的for循环、不带列表的for循环以及类C风格的for循环。本节将介绍这3种不同的for循环结构。
带列表的for循环
-
格式:
格式1:
for variable in {list}
do
statement1
statement2
...
done
格式2:
for variable in {list};do
statement1
statement2
...
done
-
分析
-
variable称为循环变量
-
list是一个列表,可以是一系列的数字或者字符串,元素之间使用空格隔开
-
do和done之间的所有的语句称为循环体,即循环结构中重复执行的语句
-
执行时,Shell会将in关键字后面的list列表的第1个元素的值赋给变量variable,然后执行循环体,当循环体中的语句执行完毕之后剩余以此类推,当list列表中的所有的元素都被访问后,for循环结构终止,程序将继续执行done语句后面的其他的语句
-
-
例1:
方法1:直接列出元素方法
[root@server ~]# vim for1.sh
#!/bin/bash
for ip in 192.168.48.130 192.168.48.131
do
echo $ip
done
[root@server ~]# bash for1.sh
192.168.48.130
192.168.48.131
方法2:使用大括号
[root@server ~]# vim for2.sh
#!/bin/bash
for ip in 192.168.48.{130..140..2} # 使用{start..end..step} 进行扩展
do
echo $ip
done
[root@server ~]# bash for2.sh
192.168.48.130
192.168.48.132
192.168.48.134
192.168.48.136
192.168.48.138
192.168.48.140
方法3:使用seq
# cat for-3.sh
#!/bin/sh
for IP in $(seq -f "192.168.1.10%1g" 1 5) 192.168.1.1 192.168.1.2
do
echo $IP
done
seq相关用法:
https://blog.youkuaiyun.com/qq_37120573/article/details/82556177
-
例2:获取指定目录下的所有目录的文件名
[root@server ~]# vim for3.sh
#!/bin/bash
for FILE in $(ls -F / | grep /$)
do
echo $FILE
done
[root@server ~]# bash for3.sh
afs/
boot/
dev/
etc/
home/
media/
mnt/
opt/
proc/
root/
run/
srv/
sys/
tmp/
usr/
var/
# ls -F :用于将目录内的文件按照文件类型进行归类显示,会在不同类别的文件名尾部增加不同的
#标识符,目录以/结尾、链接文件以@结尾、可执行文件以*结尾、普通文件无符号
# grep /$ :表示以/结尾的行
-
shell允许用户指定for语句的步长,格式如下
for variable in {start..end..step}
do
statement1
statement2
...
done
-
例3:计算1+2+3+4+5+……+100和
[root@server ~]# bash for4.sh
#!/bin/bash
sum=0
for i in {1..100..1}
do
((sum=sum+i))
done
echo "sum=$sum"
# 上例修改
[root@server ~]# bash for4.sh
#!/bin/bash
sum=0
for i in $(seq 100)
do
((sum=sum+i))
done
echo "sum=$sum"
不带列表的for循环
在某些特殊情况下,for循环的条件列表可以完全省略,称为不带列表的for循环语句。如果没有为for循环提供条件列表,Shell将从命令行获取条件列表。不带列表的for循环语句的一般语法如下:
-
原则:for不提供循环列表,shell将从命令行获取循环列表数据及条件,格式:
for variable
do
statement1
statement2
...
done
-
由于系统变量$@可以获取所有位置参数,以上的语法等价于:
for variable in $@或$*
do
statement1
statement2
...
done
-
例4:
[root@server ~]# vim for5.sh
#!/bin/bash
for arg
do
echo $arg
done
[root@server ~]# bash for5.sh {1..10}
基于C语言风格的for循环
-
格式
for ((初始化;条件表达式;步长)) # 注意:2个园括号
do
statement1
statement2
...
done
在上面的语法中,for循环语句的执行条件被2个圆括号包括起来。执行条件分为3个部分,由2个分号隔开,第1部分expression1通常时条件变量初始化的语句;第2部分expression2是决定是否执行for循环的条件。当expression2的值为0时,执行整个循环体;当expression2的值为非0时,退出for循环体。第3部分,即表达式expression3通常用来改变条件变量的值,例如递增或者递减等。
补充(常用)
++i是先i自加1,然后在使用i的值
i++是先用i的值,在i自加1。
[root@localhost ~]# x=1
[root@localhost ~]# y=1
[root@localhost ~]# echo $((x++))
1
[root@localhost ~]# echo $((++y))
2
-
例5:面试题,批量创建账户
要求:
- 用户名以test开头,按数字序号变化;
- 一共添加30个账号,即test01,tes02...,test30
- 用户初始密码为123456
分析:
根据要求使用循环语句完成
创建用户使用useradd命令,根据命名规则,10以下的需要加0
初始账号设置方法(echo “123456” |passwd –stdin username)
示例代码:user01 user10
for ((i=1;i<=30;i++))
do
if [ $i -lt 10 ]
then
user=test0$i
else
user=test$i
fi
if ! id -u $user &> /dev/null
then
useradd $user
echo "123456" | passwd --stdin $user &> /dev/null
else
echo "$user is exists..."
fi
done
注意:可以简化写法,直接用for带列表的循环,这样就不用for里面嵌套if判断
比如for i in {01..30}
-
注意:i++表示置后自增,++i置前自增
[root@server ~]# x=1
[root@server ~]# y=2
[root@server ~]# echo $((x++))
1
[root@server ~]# echo $x
2
[root@server ~]# echo $((++y))
3
[root@server ~]# echo $y
3
-
例6:编写一个 Shell 程序,实现判断当前网络(假定为192.168.1.0/24,根据实际情况实现)里,当前在线用户的IP
分析:
笔试常考题目,使用循环
for ((i=1;i<=254;i++))
do
if pint -c 2 -W 1 192.168.1.$i &>/dev/null
then
echo "192.168.1.$i is up..."
else
echo "192.168.1.$i is down..."
fi
done
while循环
while循环是另外一种常见的循环结构。使用while循环结构,可以使得用户重复执行一系列的操作,直到某个条件的发生。这听起来好像跟until循环非常相似,但是与until语句相比,while语句有着较大的区别。本节将详细介绍while语句的使用方法。
格式
while expression
do
statement1
statement1
……
done
-
在上面的语法中,expression表示while循环体执行时需要满足的条件。虽然可以使用任意合法的Shell命令,但是,通常情况下,expression代表一个测试表达式。与其他的循环结构一样,do和done这2个关键字之间的语句构成了循环体。
while循环读取文件
# 法1 ,采用exec读取文件
[root@server ~]# vim file1.txt
data1
data2
data3
data4
data5
[root@server ~]# vim while1.sh
#!/bin/bash
exec < file1.txt
while read a
do
echo $a
done
# exec < file1.txt 将file.txt文件内容读入到标准输入中
# read a 表示从标准输入中按照行读取内容到变量a
# 法2:使用cat读取文件,通过管道符进入while循环处理
cat 文件名 | while read line
do
statement1
statement1
……
done
#法3 : 在while循环尾部使用输入重定向指定输入数据
while read line
do
statement1
statement1
……
done < 文件名
变量参数是按换行符分割
示例
-
例1:猜商品价格,通过变量RANDOM获取随机整数并控制20以内,提示用户猜测商品价格,猜测时需要记录次数,猜中后退出程序
分析:
产生随机数
猜中退出循环,显然要用死循环(while true),满足条件退出程序
通过if条件判断,根据用户输入数值和生成随机数比较
记录猜测次数,每猜测一次加1
通过系统环境变量($RANDOM)产生随机数
PRICE=$[$RANDOM % 100 ]
TIMES=0
while true
do
read -p "Please enter the product price [0-99] : " INT
let TIMES++
if [ $INT -eq $PRICE ]
then
echo "Good luck,you guessed it."
echo "You have guessed $TIMES times."
exit 0
elif [ $INT -gt $PRICE ]
then
echo "$INT is too high"
else
echo "$INT is too low"
fi
done
----------------------------------------------------------------------------
利用RANDOM取随机数
shell有一个环境变量RANDOM,范围是0--32767%100
如果我们想要产生0-25范围内的数:$(($RANDOM%26)) 在$(()) 是可以省略取值的$符号的。
(All tokens in the expression undergo parameter expansion, string expansion,
command substitu-tion, and quote removal.)
用这个环境变量对26取模即可。
如果想得到1--68范围内的数 : $(($RANDOM%68+1 ))
如果想得到6--87范围内的数 : $(($RANDOM%82+6 ))
- 例2:工作过程中遇到要从一个ip列表中获取ip port然后ssh ip 到目标机器进行特定的操作
# cat iplist
192.168.147.101 5225
192.168.147.102 2234
192.168.147.103 4922
# cat while-read.sh
#!/bin/sh
while read line
do
IP=$(echo $line |awk '{print $1}')
PORT=$(echo $line |awk '{print $2}')
echo "IP: $IP, PORT: $PORT"
done <iplist
- 例3:将之前用for语句创建的test01-test30用户删除
i=1
while [ $i -le 30 ]
do
if [ $i -le 9 ]
then
user=test0$i
else
user=test$i
fi
if id -u $user &>/dev/null
then
userdel -r $user
else
echo "$user is not exists..."
fi
let i++
done
注意:while判断变量也可以使用(()),比如while ((i<=30))
until循环
until循环语句同样也存在于多种程序设计语言中。顾名思义,until语句的作用时将循环体重复执行,直到某个条件成立为止。恰当地使用until语句,可以收到事半功倍地效果。本节将详细介绍until语句的使用方法。
作用
-
until循环不断地重复执行循环中的语句,直至某个条件成立,与while循环的执行条件相反
until expreession
do
statement1
statement2
……
done
在上面的语法中,expression是一个条件表达式。当该表达式的值不为0时,将执行do和done之间的语句;当expression的值为0时,将退出until循环结构,继续执行done语句后面的其它的语句。
示例:
将之前用for语句创建的test01-test30用户删除
i=1
until [ $i -gt 30 ]
do
if [ $i -le 9 ]
then
user=test0$i
else
user=test$i
fi
if id -u $user &>/dev/null
then
userdel -r $user
else
echo "$user is not exists..."
fi
let i++
done
select循环
概念
-
select循环语句的主要功能是创建菜单,在执行带有select循环语句脚本时,输出会按照数字顺序的列表显示一个菜单,并显示提示符(默认是#?),同时等待用户输入数字选择
-
select语句的基本语法如下:
select 变量名 [ in 菜单值列表 ]
do
statement1
statement2
...
done
示例
-
例1:选择mysql版本
[root@server ~]# vim select1.sh
#!/bin/bash
select mysql_version in 5.1 5.6
do
echo $mysql_version
done
[root@server ~]# bash select.sh
1) 5.1
2) 5.6
#? 1
5.1
#? 2
5.6
#? 3
-
例2:选择水果
[root@server ~]# vim select2.sh
#!/bin/bash
PS3='Choose your favorite fruit: '
#设置提示符字符串
echo $PS3
select fruit in "Apples" "Bananas" "Pears" "Watermelons" "Grapes"
do
echo
echo "Your favorite fruit is $fruit."
break
done
exit 0
[root@server ~]# bash select2.sh
1) Apples
2) Bananas
3) Pears
4) Watermelons
5) Grapes
Choose your favorite fruit: 1
Your favorite fruit is Apples.
循环嵌套
在程序设计语言中,嵌套的循环也是一种非常常见的结构。Shell同样也支持嵌套循环。通过嵌套循环,可以完成更复杂的功能。本节将介绍Shell中嵌套循环的使用方法。
注意:校招常考题目。
-
例1:打印九九乘法表
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
.....
#行 9 i=2
#列 ((j=1;j<=i;j++)) j=1 j=2
1*1
1*=2 2*2=4
.....
j*i=$((j*i))
for i in `seq 9` #i循环次数 i=被乘数
do
for j in `seq 9` #j乘数 ++ 一行循环的次数
do
[ $j -le $i ] && echo -n "$j*$i= `echo $(($i*$j))` "
done
echo " "
done
#############################################################################
for ((i=1;i<10;i++));do
for ((j=1;j<=i;j++));do
echo -n "$j * $i = `let $i \* $j`"
done
echo ""
done
-
例2:打印数字三角形
分析:
使用双层循环
外层循环控制输出行数i:9次 i=1 i<=9 i++ 1
内层循环:
第一个循环打印每行空格j j=1 j<=10-i j++
第二个循环打印数字k k=1 k<=i k++ 1
i(取值) 1 2 3 ... 9
j(个数) 9 8 7 1 j<=10-i
k(个数) 1 2 3 9 k<=i
for ((i=1; i<10; i++))
do
for ((j=1; j<=10-i; j++))
do
echo -n " ";
done
for ((k=1; k<=i; k++))
do
echo -n "$i "
done
echo ""
done
运行后:
1
2 2
3 3 3
4 4 4 4
5 5 5 5 5
6 6 6 6 6 6
7 7 7 7 7 7 7
8 8 8 8 8 8 8 8
9 9 9 9 9 9 9 9 9
循环控制
组成
-
break: 是立即跳出某个循环结构。break语句可以用在for、while或者until等循环语句的循环体中
-
continue: 作用不是退出循环体。而是跳过当前循环体中该语句后面的语句,重新从循环语句开始的位置执行
详细语法:
示例
-
例1:判断素数
[root@server ~]# vim for7.sh
#!/bin/bash
read -p "请输入一个正整数: " prime
if ((prime<2))
then
echo "数据错误,请输入大于等于2的正整数."
else
for ((i=2;i<prime;i++))
do
if ((prime%i==0))
then
echo "平数"
break
fi
done
if ((prime==i))
then
echo "素数"
fi
fi
-
例2:计算100到150之间不能被3整除的数,输出时一行显示5个
#!/bin/bash
count=0
for ((i=100;i<=150;i++))
do
if ((i%3==0))
then
continue
fi
echo -ne "$i\t"
((count++))
if ((count%5==0))
then
echo
fi
done
echo