Bash脚本编程学习笔记07:循环结构体
本篇中涉及到算术运算,使用了$[]这种我未在官方手册中见到的用法,但是确实可用的,在此前的博文《Bash脚本编程学习笔记03:算术运算》中我有说明不要使用,不过自己忘记了。大家还是尽量使用其他的方法进行算术运算。
简介
Bash具有三种循环结构:
- for循环。
- while循环。
- untile循环。
在使用循环结构体的时候,需要注意循环的进入条件和结束条件,避免出现死循环的情况。
for循环
for循环又分为两种格式:遍历列表和控制变量。
遍历列表
for VAR in LIST; do
BODY
done
VAR:变量,在每次循环时,都会被LIST中的元素所赋值。
LIST:列表,生成列表的方式有多种,后面会详述。
BODY:循环体,由各种各样的命令所构成,在循环体中会引用变量VAR。
进入循环条件:只要LIST中有元素即可。
离开循环条件:LIST中的元素遍历完毕。
LIST的生成方式
这里的LIST生成方式的分类并非官方,甚至看起来蛮重复的,大家实际敲过之后就大概能明白列表是个什么东西了。
官方的LIST其实就是shell展开。
1、直接给出。
[root@c7-server ~]# cat for_list.sh
#!/bin/bash
for i in ; do
echo $i
done
[root@c7-server ~]# bash for_list.sh
2、通过大括号或者seq命令生成的整数列表。
[root@c7-server ~]# cat for_list.sh
#!/bin/bash
for i in {..}; do
echo $i
done
[root@c7-server ~]# bash for_list.sh
seq是一个外部命令,一般用于生成一个整数序列。
seq [FIRST [INCREMENT]] LAST
见名知意,大家试试以下几种命令生成的整数列表就懂了。
# seq
# seq
# seq
# seq
# seq
seq在使用的时候要结合bash的命令替换机制,即下面要说的就是了。
3、引用返回列表的命令。
像刚才的seq其实就类似于这种,其他的例如应用ls命令的结果的也是可以。
4、Glob风格的展开。
----2020-01-13----
{x..y}:当x和y是正整数的时候,相比大家都会用,需要注意的是它可以加入一个增量。
{x..y[..incr]}
示例如下:
[root@c7-server ~]# echo {..}
[root@c7-server ~]# echo {....}
5、位置参数($@, $*)的引用。
练习
1、编写一个脚本,批量创建5个用户。
#!/bin/bash
for i in zwl0{..}; do
if id $i &> /dev/null; then
echo "User $i has already exists."
else
useradd $i
fi
done
2、编写一个脚本,统计100以内的整数之和、奇数之和与偶数之和。
整数之和。
#!/bin/bash declare -i sum=
for i in $(seq 100); do
echo "sum is $sum, i is $i."
sum=$[$sum+$i]
done
echo "sum is $sum."
奇数之和:只要将seq替换为“seq 1 2 100”即可。
偶数之和:只要将seq替换为“seq 2 2 100”即可。
3、编写一个脚本,判断某目录下的所有文件的类型。
#!/bin/bash for i in /root/*; do
file $i
done
其实file命令本身即可实现,主要是了解一下可以以通配符展开来生成LIST。
# file /root/*
4、计算当前所有用户的UID之和。
#!/bin/bash declare -i sum=
for i in $(cut -d : -f /etc/passwd); do
sum=$[$sum+$i]
done
echo "The sum of UIDs is $sum."
5、统计某个目录下的文本文件总数,以及文本文件的行数之和。注:无需递归,仅统计目录下第一层即可。
#!/bin/bash if [ ! -d $ ]; then
echo "The file you input is not a directory,exit!"
exit
fi declare -i textCount=
declare -i lineCount= for i in $/*; do
if [ -f $i ]; then
lines=$(wc -l $i | cut -d " " -f 1)
textCount=$[$textCount+1]
lineCount=$[$lineCount+$lines]
fi
done echo "The number of text file is $textCount."
echo "The number of line of text file is $lineCount."
控制变量
for语句的控制变量,其实就是类似于C风格的for语句。
for ((expr1; expr2; expr3)); do
BODY
done
此类for语句,只用于数值类的计算,写起来更像C语言,在(())不需要再使用$对变量进行展开,写起来更简洁方便。
expr1:只有在第一次循环时执行,一般用于对某个变量进行赋初值的操作。
expr2:每次都会执行,一般是对赋值的变量进行条件判断,为真执行BODY;为假的话,结束循环。
expr3:对赋值的变量进行数值调整,使其将来满足expr2为假的情况从而结束循环。
示例:计算100以内所有奇数之和。
#!/bin/bash declare -i sum=
for ((i=;i<=;i+=)); do
((sum+=i))
done
echo "Sum is $sum."
while循环
while CONDITION; do
CMD
done
当CONDITION为真时,执行CMD,直到CONDITION为假的时候才退出循环。
CMD中一般会包含一些可以在将来改变CONDITION的判定结果的操作,否则会出现死循环。
while循环相比for循环的优势在于,for循环需要事先生成一个列表,如果列表元素比较大,例如{1..10000},那么就会占用比较多的内存空间;而while循环只需要占用一个变量的内存空间即可。(结论来自马哥,我觉得应该没错吧)
我们使用while循环来实现计算100以内的正整数之和。
[root@c7-server ~]# cat while_sum.sh
#!/bin/bash declare -i sum=
declare -i i=
while [ $i -le ]; do
((sum+=i))
((i++))
done echo "sum is $sum."
几个注意事项:
后缀自增/减运算和赋值(如:+=, -=, *=, /=等等)时,涉及变量的时候不要将变量展开,否则会报错。
[root@c7-server ~]# declare -i i=
[root@c7-server ~]# (($i++))
-bash: ((: ++: syntax error: operand expected (error token is "+")
[root@c7-server ~]# (($i+=))
-bash: ((: +=: attempted assignment to non-variable (error token is "+=1")
同样的方式,在前缀自增/减时虽然不会报错,但是也不会达到预期的效果。
[root@c7-server ~]# ((++$i))
[root@c7-server ~]# echo $i
因为一旦使用了展开,bash会先进行展开,再进行算术运算。展开后就变成了。
((++))
((++))
((+=))
正确的用法是:
((i++))
((++i))
((i+=))
赋值时,等于号右边的变量,可以不展开。以下是等效的。
((a=a+b))
((a=$a+$b))
((a+=b))
((a+=$b))
特殊用法:遍历文件内容
while循环有一种特殊的用法,可以遍历文件的内容(以行为单位),进行处理。文件内容遍历完毕后退出。
while read VAR; do
BODY
done < /PATH/FROM/SOMEFILE
示例:打印UID为偶数的用户的名称、UID和默认shell。
#!/bin/bash while read line; do
userName=$(echo $line | cut -d : -f )
userID=$(echo $line | cut -d : -f )
userShell=$(echo $line | cut -d : -f )
if [ $((userID%)) -eq ]; then
echo "User name is $userName. User ID is $userID. User shell is $userShell."
fi
done < /etc/passwd
使用for+cat命令替换也可以实现类似的功能。
for i in $(cat /PATH/TO/SOMEFILE); do
echo $i
done
文件中的每一行也会被赋值给i,但是如果行内存在空格,那么那一行会被理解为多行。因此比较稳妥的方法还是使用while的特殊用法。
until循环
untile CONDITION; do
CMD
done
与while循环的进入循环和退出循环的逻辑正好相反。当CONDITION为假时,执行CMD,直到CONDITION为真的时候才退出循环。
同样,CMD中一般会包含一些可以在将来改变CONDITION的判定结果的操作,否则会出现死循环。
until循环和while循环只是逻辑相反,因此用的比较少,while比较常用。
同样我们也使用until循环实现100以内的正整数之和的计算。
#!/bin/bash declare -i sum=
declare -i i=
until [ $i -gt ]; do
((sum+=i))
((i++))
done echo "sum is $sum."
练习
1、使用四种循环结构体实现乘法口诀表的正向和反向打印。
for循环正向打印。
[root@c7-server ~]# cat for_jiujiu.sh
#!/bin/bash for i in {..}; do
for j in $(seq $i); do
echo -ne "$j*$i=$((i*j))\t"
done
echo
done
[root@c7-server ~]# bash for_jiujiu.sh
*=
*= *=
*= *= *=
*= *= *= *=
*= *= *= *= *=
*= *= *= *= *= *=
*= *= *= *= *= *= *=
*= *= *= *= *= *= *= *=
*= *= *= *= *= *= *= *= *=
这里需要注意的一点,是:
for j in $(seq $i)
不可以换成
for j in {..$i}
具体示例如下。
[root@c7-server ~]# echo {..}
[root@c7-server ~]# declare -i i=
[root@c7-server ~]# echo {..$i}
{..}
造成此结果的原因,在官方的关于大括号展开中有提及。
Brace expansion is performed before any other expansions, and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces.
for循环的反向打印,只要将{1..9}换成{9..1}即可。
[root@c7-server ~]# bash for_jiujiu.sh
*= *= *= *= *= *= *= *= *=
*= *= *= *= *= *= *= *=
*= *= *= *= *= *= *=
*= *= *= *= *= *=
*= *= *= *= *=
*= *= *= *=
*= *= *=
*= *=
*=
C风格的for循环的正向打印。
#!/bin/bash for ((i=;i<=;i++)); do
for ((j=;j<=i;j++)); do
echo -ne "$j*$i=$((i*j))\t"
done
echo
done
C风格的for循环的反向打印。
#!/bin/bash for ((i=;i>=;i--)); do
for ((j=;j<=i;j++)); do
echo -ne "$j*$i=$((i*j))\t"
done
echo
done
while循环正向打印。
#!/bin/bash declare -i i=
while [ $i -le ]; do
declare -i j=
while [ $j -le $i ]; do
echo -ne "$j*$i=$((i*j))\t"
((j++))
done
((i++))
echo
done
while循环反向打印。
#!/bin/bash declare -i i=
while [ $i -gt ]; do
declare -i j=
while [ $j -le $i ]; do
echo -ne "$j*$i=$((i*j))\t"
((j++))
done
((i--))
echo
done
until循环正向打印。
#!/bin/bash declare -i sum=
declare -i i=
until [ $i -gt ]; do
declare -i j=
until [ $j -gt $i ]; do
echo -ne "$j*$i=$((i*j))\t"
((j++))
done
((i++))
echo
done
until循环反向打印。
#!/bin/bash declare -i sum=
declare -i i=
until [ $i -le ]; do
declare -i j=
until [ $j -gt $i ]; do
echo -ne "$j*$i=$((i*j))\t"
((j++))
done
((i--))
echo
done
循环控制指令
此前讲解循环时,循环的每一轮,都是要执行的,直到循环退出条件满足时才退出。而循环控制指令continue和break,可以改变这种默认的机制。
continue:结束本轮循环,进入下一轮循环。大致结构如下。
for i in LIST; do
CMD1
...
if TEST; then
continue
fi
CMDn
...
done
进入下一轮循环的条件是本轮循环的BODY部分全部执行完,因此continue不应该放在整个BODY的末尾,否则就有点画蛇添足了。
continue一般放在BODY的中间,结合某些判断跳出本轮循环。例如,当LIST是100以内的所有正整数时,求所有偶数之和。
#!/bin/bash declare -i sum=
for i in {..}; do
if [ $((i%)) -eq ]; then
continue
fi
((sum+=i))
done
echo "sum is $sum."
break:直接结束所有的循环,继续执行脚本剩余的部分。这里要注意和exit区分,如果break出现的位置换成了exit的话,那么exit结束的是整个脚本,而不仅仅是循环而已,脚本直接退出了,不会执行脚本剩余部分。
break一般会结合死循环一起使用,死循环一般会结合sleep命令一起使用。
sleep命令基本语法。
sleep n[suffix]
n:具体的数值,默认单位是秒。
suffix:后缀,表示单位。s秒、n分钟、h小时、d天数。
大致的语法就是这个样子,整个循环是一个死循环。sleep控制了死循环的循环间隔,防止消耗资源过多;if+break实现了对死循环的控制,达到某个条件就退出。
while true; do
CMD1
...
if TEST; then
break
fi
[CMDn
...]
sleep ...
done
使用死循环+break求100以内所有奇数之和。
#!/bin/bash declare -i i=
declare -i sum=
while true; do
if [ $i -gt ]; then
break
fi
((sum+=i))
((i+=))
done
echo "sum is $sum."
练习:每隔3秒监控系统中已登录的用户,如果发现alongdidi则记录于日志中并退出脚本。
思路一:死循环监控
[root@c7-server ~]# cat user_monitor.sh
#!/bin/bash while true; do
if who | grep "^alongdidi\>" &> /dev/null; then
echo "$(date +"%F %T") alongdidi logined." >> /var/log/user_monitor.log
break
else
sleep
fi
done
[root@c7-server ~]# cat /var/log/user_monitor.log
-- :: alongdidi logined.
思路二:直到alongdidi登录,否则继续循环。
#!/bin/bash until who | grep "^alongdidi\>" &> /dev/null; do
sleep
done
echo "$(date +"%F %T") alongdidi logined." >> /var/log/user_monitor.log
continue和break在使用的时候可以带上参数n。
continue n:跳出n轮循环。
break n:结束几个循环体。这种在嵌套循环的情况下才会遇到。
while ...; do
while ...; do
break
done
done
Bash脚本编程学习笔记07:循环结构体的更多相关文章
- Bash脚本编程学习笔记06:条件结构体
简介 在bash脚本编程中,条件结构体使用if语句和case语句两种句式. if语句 单分支if语句 if TEST; then CMD fi TEST:条件判断,多数情况下可使用test命令来实现, ...
- Bash脚本编程学习笔记08:函数
官方资料:Shell Functions (Bash Reference Manual) 简介 正如我们在<Bash脚本编程学习笔记06:条件结构体>中最后所说的,我们应该把一些可能反复执 ...
- Bash脚本编程学习笔记05:用户交互与脚本调试
用户交互 在<学习笔记04>中我们有提到位置参数,位置参数是用来向脚本传递参数的一种方式.还有一种方式,是read命令. [root@c7-server ~]# read name alo ...
- Bash脚本编程学习笔记04:测试命令test、状态返回值、位置参数和特殊变量
我自己接触Linux主要是大学学习的Turbolinux --> 根据<鸟哥的Linux私房菜:基础篇>(第三版) --> 马哥的就业班课程.给我的感觉是这些课程对于bash的 ...
- bash脚本编程学习笔记(一)
bash脚本语言,不同于C/C++是一种解释性语言.即在执行前不需要事先转变为可执行的二进制代码,而是每次执行时经解释器解释后执行.bash脚本语言是命令的堆砌,即按照实际需要,结合命令流程机制实现的 ...
- bash脚本编程学习笔记(二)
1.脚本编程之函数 函数是实现结构化编程重要的思想,主要目的是实现代码重用 定义一个函数: function FUNCNAME { command //函数体 } FUNCNAME(){ //函数 ...
- Go语言学习笔记十: 结构体
Go语言学习笔记十: 结构体 Go语言的结构体语法和C语言类似.而结构体这个概念就类似高级语言Java中的类. 结构体定义 结构体有两个关键字type和struct,中间夹着一个结构体名称.大括号里面 ...
- matlab学习笔记12_3串联结构体,按属性创建含有元胞数组的结构体,filenames,isfield,isstruct,orderfields
一起来学matlab-matlab学习笔记12 12_3 结构体 串联结构体,按属性创建含有元胞数组的结构体,filenames,isfield,isstruct,orderfields 觉得有用的话 ...
- Swift 学习笔记 (类和结构体)
类和结构体是一种多功能且灵活的构造体.通过使用与现存常量 变量 函数完全相同的语法来在类和结构体中定义属性和方法以添加功能. Swift中不需要你为自定义的类和结构体创建独立的结构和实现文件.在Swi ...
随机推荐
- nginx之基础安装
前言 nginx的安装方式可能不同,具体取决于操作系统:对于Linux,可以使用nginx.org的nginx软件包.在FreeBSD上,可以从软件包或通过端口系统安装nginx. 端口系统提供了更大 ...
- 暑假第七周总结(安装MongoDB和Tomcat以及MongoDB进行编程实践)
本周主要对MongoDB和Tomcat进行了安装,两项安装都遇到了一些问题.其中在对MongoDB安装过程中出现了什么没有秘钥安全证书的,最终找了一堆教程重复了好多遍之后安装成功,虽然在启动和关闭的时 ...
- Asp.net core 2.x/3.x 的 Globalization 和 localization 的使用 (一) 使用方法
由于Api的接口需要返回多语言,因此参考了网上很多篇文章,,有些文章写的太过于理论,看起来比较费劲,今天下午搞了一个下午,总结了一下经验,, 做这个功能时,主要参考了两篇文章: https://blo ...
- SpringBoot之切面AOP
SpringBoot提供了强大AOP支持,我们前面讲解过AOP面向切面,所以这里具体AOP原理就补具体介绍: AOP切面主要是切方法,我们一般搞一些日志分析和事务操作,要用到切面,类似拦截器: @As ...
- 二、Mongodb常用命令
#进入admin数据库 use admin #进行权限认证 db.auth('userAdmin', '123456') #查询所有用户 db.system.users.find() #更新用户 db ...
- git命令清单 摘自 阮老师
常用 Git 命令清单 作者: 阮一峰 日期: 2015年12月 9日 我每天使用 Git ,但是很多命令记不住. 一般来说,日常使用只要记住下图6个命令,就可以了.但是熟练使用,恐怕要记住60- ...
- 1. 学习Linux操作系统
1.熟练使用Linux命令行(鸟哥的Linux私房菜.Linux系统管理技术手册) 2.学会Linux程序设计(UNIX环境高级编程) 3.了解Linux内核机制(深入理解LINUX内核) 4.阅读L ...
- Django运行方式及处理流程总结(转发)
之前在网上看过一些介绍Django处理请求的流程和Django源码结构的文章,觉得了解一下这些内容对开发Django项目还是很有帮助的.所以,我按照自己的逻辑总结了一下Django项目的运行方式和对R ...
- 浅谈Go语言的Goroutine和协程
0x00.前言 前面写了一篇初识Go语言和大家一起学习了Go语言的巨大潜力.语言简史.杀手锏特性等,感兴趣的读者可以回顾一下. 今天来学习Go语言的Goroutine机制,这也可能是Go语言最为吸引人 ...
- Linux学习1-云服务器上搭建禅道项目管理工具
前言 相信各位测试的小伙伴出去面试总会被问到:测试环境怎么搭建?一个中级测试工程师还是对测试环境一无所知的话,面试官会一脸鄙视的,今天我给大家介绍一下最简单的环境部署-—如何在云服务器部署禅道环境. ...