shell中for、while、select循环

一、流程控制之for循环

1.1 语法

#===========》Shell风格语法
for 变量名 [ in 取值列表 ]
do
	循环体
done

#===========》C语言风格语法
for ((初值;条件;步长))
do	
	循环体
done

#==============》Python风格
for i in (可迭代对象)
	print(i)

shell风格的for,常用in列表方式

for i in 1 2 3
for i in {1,2,3}
for i in {1..9}
for i in {9..1}
for i in {a..z}
for i in {A..Z}
for i in {X..Z}
for i in $(命令)  # 例如:for i in $(head -10 /etc/passwd);do echo $i|cut -d: -f1,2;done
for i in $(find ...)

1.2 for循环取值方式

for 循环默认使用空格为分隔符,可以使用 IFS 来自定义分隔符
以冒号做分隔符 IFS=:
以换行符做为字段分隔符IFS=$'\n'

默认是以空格为分隔符取值
[root@manager for]# cat test2.sh  
for i in $(cat /etc/hosts)
do
	echo $i
done

[root@manager for]# sh test2.sh 
127.0.0.1
localhost
localhost.localdomain
localhost4
localhost4.localdomain4
::1
localhost
localhost.localdomain
localhost6
localhost6.localdomain6

修改为换行符取值

[root@manager for]# cat test2.sh 
IFS=$'\n'
for i in $(cat /etc/hosts)
do
	echo $i
done

[root@manager for]# sh test2.sh 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

1.3 for循环通过文件创建用户

[root@manager for]# cat user.txt 
tom1:123
tom2:123
tom3:123
tom4:123
tom5:123

[root@manager for]# cat adduser.sh
for i in $(cat ./user.txt)
do
	username=$(echo $i | awk -F ":" '{print $1}')
	passwd=$(echo $i | awk -F ":" '{print $2}')

	id $username
	if [ $? -eq 0 ];then
		echo "用户已存在"
		continue
	else
		useradd $username
		echo "$passwd" | passwd --stdin $username
		echo "$username 创建成功"
	fi
	sleep 1
done

1.4 输出整数升序降序

[root@manager for]# cat sort.sh 
a=10
b=0
for i in $(seq 1 10)
do
	let a--
	let b++
	echo $a------$b
done

1.5 100以内能被3整除数之和

[root@manager for]# cat division.sh 
sum=0
for i in {1..100}
do
	remainder=$(echo $i%3 | bc )
	if [ $remainder -eq 0 ];then
		sum=$[ $sum + $i ]
	fi
done
echo $sum

[root@manager for]# sh division.sh 
1683

1.6 for循环计算1+2+3+n的值

[root@manager for]# cat sum.sh 
sum=0
for i in {1..100}
do
	sum=$[ $sum+$i ]
done

echo $sum

1.7 探测主机存活性

# 批量生成ip命令
[root@manager for]# seq 1 254 | sed -r 's/(.*)/172.16.1.\1/' > /tmp/ip.txt


[root@manager for]# cat ip.sh 
for i in $(cat /tmp/ip.txt)
do
	{	
	ping $i -c1 &>/dev/null
	if [ $? -eq 0 ];then
		echo "$i is ok"
	else
		echo "$i is unavaliabled"
	fi

	}&

done

	wait

echo "done"

1.8 探测ip是否存活

1.判断是否存活;
2.存活,则直接输出;
3.不存活,在探测三次;

[root@manager for]# cat ip3.sh 

# 定义探测函数
test_ip(){
	for i in {1..3}
	do
		ping $1 -c1 &>/dev/null
		if [ $? -eq 0 ];then
			echo "$1尝试第$[ $i+1 ]次才通"
			break
		else
			echo "$1尝试第$[ $i+1 ]次仍然不通 "
		fi
	done
}


for i in {1..254}
do
{
	ip=172.16.1.$i

	ping $ip -c1 &>/dev/null

	if [ $? -eq 0 ];then
		echo "$ip is ok"

	else
		test_ip $ip # 调用测试函数
	fi
}&
done
	wait
echo "done............................"

1.9 探测主机开放哪些端口

1.有一个ip.txt的文件,里面有很多IP地址。
2.有一个port.txt的文件,里面有很多端口号。
3.现在希望对ip.txt的每个IP地址进行port.txt文件中的端口号进行挨个探测。
4.最后将开放的端口和IP保存到一个ok.txt文件。

[root@manager for]# cat test.sh 
for ip in $(cat ip.txt)
do
	for port in $(cat port.txt)
	do
		nc -z $ip $port &>/dev/null  # 探测端口
			if [ $? -eq 0 ];then
				echo "$ip:$port" >> /opt/ip_port.txt
				if [ $port -eq 80 ];then
						echo -e "\e[31m $ip 的 $port开放了,比较危险..\e[0m"
				else
					echo -e "\e[33m $ip 的 $port开放了\e[0m"
				fi
			fi
	done

done

在这里插入图片描述

1.10 获取系统用户,并按照编号输出

[root@web02 ~]# cat print_user.sh 
IFS=$'\n'
for i in $(cat /etc/passwd)
do
	user=$(echo $i | awk -F ":" '{print $1}')
	let k++
	echo "This is user:$user  $k"	

done

1.11 获取普通用户的基本组及附加组

[root@manager ~]# cat group.sh 
user=$(awk -F ":" '$3>=1000{print $1}' /etc/passwd)
for user in $user
do
	flag=$(id $user | grep , )
	if [ -z "$flag" ];then # 判断是否有附加组
		group=$(id $user | awk  -F "[ =]" '{print $NF}')
		groups=null
	else
		group=$(id $user | awk  -F "[ =,]" '{print $(NF-2)}')
		groups=$(id $user | awk  -F "[ =,]" '{print $(NF-1),$NF}')
	fi
		
	echo "$user的基本组为:$group"
	echo "$user的附加组为:$groups"

done

# 添加两个基本用户,一个有附加组,一个没有
[root@manager for]# groupadd ops
[root@manager for]# groupadd dev
[root@manager for]# useradd tom -u 6666  -G dev,ops -c "good man" -s /bin/shell
[root@manager for]# id tom
uid=6666(tom) gid=6666(tom) groups=6666(tom),1005(ops),1006(dev)

[root@manager for]# useradd jack -u 6667 
[root@manager for]# id jack
uid=6667(jack) gid=6667(jack) groups=6667(jack)


[root@manager ~]# sh group.sh 
nfsnobody的基本组为:65534(nfsnobody)
nfsnobody的附加组为:null
tom5的基本组为:1004(tom5)
tom5的附加组为:null
tom的基本组为:6666(tom)
tom的附加组为:1005(ops) 1006(dev)
jack的基本组为:6667(jack)
jack的附加组为:null

1.12 备份数据库,分库

#!/usr/bin/bash
. /etc/init.d/functions

Db_Name=$(mysql -uroot -e "show databases;" | sed 1d | egrep -v "*_schema")
Date=$(date +%F)
DB_Dir=/backup/mysql/${Date}

# 确保备份的目录是存在的
if [ ! -d ${DB_Dir} ];then
	mkdir -p ${DB_Dir}	
fi

# 备份业务逻辑
for database in ${Db_Name}
do
	mysqldump -uroot -B ${database} > ${DB_Dir}/${database}.sql
	
	# 判断文件是否有内容
	if [ -s ${DB_Dir}/${database}.sql ];then
		action "${DB_Dir}/${database}.sql 备份成功" /bin/true
	else
		action "${DB_Dir}/${database}.sql 备份失败" /bin/true
	fi
done

1.13 备份数据库,分库分表

#!/usr/bin/bash
. /etc/init.d/functions

DB_Name=$(mysql -uroot -e "show databases;" | sed 1d | egrep -v "*_schema")
Date=$(date +%F)
DB_Dir=/backup/mysql/${Date}

# 外循环,提取库的名称
for database in ${DB_Name}
do
	# 创建库对应的备份目录
	if [ ! -d $DB_Dir/$database ];then
		mkdir -p "$DB_Dir/$database"
	fi

	# 提取表名称
	TB_Name=$(mysql -e "use ${database};show tables;" | sed 1d)
	
	# 内循环,基于库名称然后取获取表名称
	for table in ${TB_Name}
	do
		mysqldump -uroot ${database} ${table} > $DB_Dir/${database}/${table}.sql
		if [ -s $DB_Dir/${database}/${table}.sql ];then
			action "$DB_Dir/${database}/${table}.sql 备份成功" /bin/true
		else
			action "$DB_Dir/${database}/${table}.sql 备份失败" /bin/false
		fi
	done
done

1.14 九九乘法表

1.python实现

for i in range(1, 10):
    for j in range(1, i + 1):
        print('{}*{}={} '.format(i, j, i * j), end="")
    print()

2.shell实现

[root@manager for]# cat multi.sh 
for i in {1..9}
do
	for j in {1..9}
	do
		if [ $j -le $i ];then # 小于等于才打印
			echo -n "$i*$j=$(echo "$i*$j" | bc) "
		fi
	done
	echo '' #换行
done
[root@manager for]# sh multi.sh 
1*1=1 
2*1=2 2*2=4 
3*1=3 3*2=6 3*3=9 
4*1=4 4*2=8 4*3=12 4*4=16 
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 

1. 5 模拟删除tomcat 后恢复文件

[root@web01 /]# cd soft/ 
[root@web01 soft]# cp  -rp apache-tomcat-9.0.52 /opt/ # 将tomcat拷贝一份到/opt 并做软连接
[root@web01 soft]# cd /opt/
[root@web01 opt]# ln -s apache-tomcat-9.0.52/ tomcat

[root@web01 opt]# /opt/tomcat/bin/startup.sh  # 启动tomcat
[root@web01 opt]# ps aux | grep tomcat
root       41945  0.1 11.0 2275268 110012 ?      Sl   Sep19  14:55 /usr/local/jdk/bin/java -Djava.util.logging.config.file=/soft/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -classpath /soft/tomcat/bin/bootstrap.jar:/soft/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/soft/tomcat -Dcatalina.home=/soft/tomcat -Djava.io.tmpdir=/soft/tomcat/temp org.apache.catalina.startup.Bootstrap start
root      41829  0.0  0.0 112812   976 pts/0    S+   20:05   0:00 grep --color=auto tomcat


[root@web01 opt]# rm -rf tomcat/ # 删除
[root@web01 opt]# ll /proc/41945/fd

在这里插入图片描述
可以看到,删除后文件描述符对应的文件闪烁,而且 都带有delete后缀

[root@web01 opt]# ll /proc/41945/fd | grep deleted

而且java是个多线程的程序

[root@web01 opt]# pstree -p 41945
java(41945)─┬─{java}(41946)
            ├─{java}(41947)
            ├─{java}(41948)
            ├─{java}(41949)
            ├─{java}(41950)
            ├─{java}(41951)
            ├─{java}(41952)
            ├─{java}(41953)
            ├─{java}(41954)
            ├─{java}(41955)
            ├─{java}(41956)
            ├─{java}(41957)
            ├─{java}(41961)
            ├─{java}(41962)
            ├─{java}(41963)
            ├─{java}(41964)
            ├─{java}(41984)
            ├─{java}(42007)
            ├─{java}(42008)
            ├─{java}(42009)
            └─{java}(42016)

也就是说,我们还需要过滤出所有线程id 下对应的/proc/线程号/fd中带有deleted标志的文件

[root@web01 opt]# mkdir /opt/apache-tomcat-9.0.52/{lib,bin,logs} # 创建对应的目录
[root@web01 opt]# ll /proc/41945/fd | grep delete | awk '{print $9}' # 获取第9列的文件描述符
[root@web01 opt]# ll /proc/41945/fd | grep delete | awk '{print $11}' # 获取文件名


[root@web01 ~]# cat recorve.sh 
#!/bin/bash
. /etc/init.d/functions

# java 进程id
pid=41945

# 过滤出java进程和所有线程id
thread_id=$(pstree -p 41945 | egrep -o "[0-9]+")

#遍历所有线程
for id in $thread_id
do
	# 取每个线程下的文件描述符	
	fd=$(ls -l /proc/$id/fd | grep delete | awk '{print $9}')
	
	
	for i in $fd
	do
	     # 基于文件描述符获取文件名称
		file_name=$(ls -l /proc/$id/fd/$i | awk '{print $11}')
		
		#echo  $file_name
		# 恢复文件
		cat /proc/$id/fd/$i > $file_name
		
		# 获取恢复后文件的md5值
		remd5=$(md5sum "$file_name" 2>/dev/null  | awk '{print $1}')
		
	
		#源文件名,真实情况下已删除后就不存在了
		# 变量替换
		old_file_name=${file_name/opt/soft}	
		#echo $old_file_name
	
		# 获取源文件MD5值,真实情况不存在
		old_md5=$(md5sum "$old_file_name" 2>/dev/null | awk '{print $1}')
		if [ $remd5 == $old_md5 ];then
			action "$file_name 恢复成功" /bin/true
		else
			action "$file_name 恢复失败"  /bin/false
		fi
	done

done

运行部分截图:
在这里插入图片描述

二、流程控制之while循环

2.1 语法:

# 一、while语句结构:条件为真时,执行循环体代码
while 条件
do
	循环体
done

# 二、until语法结构:条件为假时,一直执行循环体代码,直到条件变为真
until 条件
do
	循环体
done

示例:

[root@manager while]# cat a.sh 
#!/bin/bash

x=0
while (($x < 3))
do
    echo $x
    let x++
done

echo "================"

y=0
until (($y == 3))
do
    echo $y
    let y++
done

[root@manager while]# ./a.sh 
0
1
2
================
0
1
2

2.1 降序输出10到1的数字

[root@manager while]# cat sort.sh 
var=10

while (( $var>0 ))
do
	echo $var
	 var=$[ $var-1 ]

done

2.2 网站探测

[root@manager while]# cat url.sh 
#!/bin/bash
timeout=3
fails=0
success=0
url=$1

while true
do
    echo "=====>$[ $fails+1]"

    if [ $fails -eq 3 ]
    then
        echo "页面前访问3次均失败"
        break
    fi

    wget --timeout=$timeout --tries=1 http://$url -q

    if [ $? -ne 0 ]
    then
       let fails++
    else
       echo "页面访问成功"
       break
    fi

done


[root@manager while]# sh url.sh www.baidu.com
=====>1
页面访问成功
[root@manager while]# sh url.sh xxx.baidu.com
=====>1
=====>2
=====>3
=====>4
页面访问3次均失败,本次退出

2.3 猜数字

方法一: 通过random变量产生随机数  (0-32768)
echo $RANDOM

方法二: 通过openssl命令产生随机数
openssl rand -base64 10

方法三: 通过时间信息获取随机数
date +%S%N

方法四: 通过一个特殊设备文件生成随机数
head -c9 /dev/urandom|cksum
tr -dc 0-9 < /dev/urandom | head -c8

方法五: 利用UUID文件生成随机数
cat /proc/sys/kernel/random/uuid


# 代码实现
[root@manager while]# cat guess_age.sh 
#!/bin/bash

num=`echo $((RANDOM%100+1))`  # 对100取余,生成1100的随机数

count=0
while :
do
    [ $count -eq 3 ] && echo "猜的次数超过3次,退出" && exit
    read -p "请输入[1-100]之间的一个数字:" x
    [[ ! $x =~ ^[0-9]+$ ]] && echo "必须输入数字" && continue
    if [ $x -gt $num ];then
        echo "猜大了"
    elif [ $x -lt $num ];then
        echo "猜小了"
    else
        echo "猜对了"
        break
    fi
    let count++
done

2.4 while c语言风格写法

[root@manager while]# cat 1.sh
#!/bin/bash
i=1
while ((i<10))
do
    echo $i
    ((i++))
done

[root@manager while]# sh 1.sh
1
2
3
4
5
6
7
8
9

2.5 循环控制

在使用循环语句进行循环的过程中,有时候需要在未达到循环结束条件时强制跳出循环;那么 Shell 给我们提供了内置方法来实现该功能:exit、break、continue

  • 当脚本碰到 exit 时,直接退出,剩余不管有多少代码都不执行
  • 当脚本碰到 break 时,会跳出当前循环,但会执行循环之后的所有的代码。
  • 当脚本碰到 continue 时,跳过当前循环,直接进入下一次循环,直到循环结束,然后继续执行循环之后的代码。

2.5.1 打印1-9,当数值为5则跳过本次循环,继续下一次循环。

请分别使用for和while实现

[root@manager while]# cat for.sh 
for i in {1..9}
do
	if [ $i -eq 5 ];then
		continue
	else
		echo $i
	fi	
	
done

[root@manager while]# cat while.sh 
i=0
while (($i<10))
do
	#((i++))
	let i++
	if (($i==5));then
		continue
	fi
	echo $i
done


2.5.2 破解随机字符串

字符串 efbaf275cd、4be9c40b8b、44b2395c46是通过对随机数变量RANDOM随机执行命令: echo $RANDOM|md5sum|cut -c1-10后的结果请破解这些字符串对应的RANDOM值

[root@manager while]# cat track.sh 
for i in efbaf275cd 4be9c40b8b 44b2395c46
do
	num=0
	while true
	do
		str=$(echo $num|md5sum|cut -c1-10)
		if [ $str == "$i" ];then
			echo "$num加密后的结果是$i"
			break
		fi
		num=$[ $num + 1 ]	
	done
done 

[root@manager while]# sh track.sh 
15000加密后的结果是efbaf275cd
12000加密后的结果是4be9c40b8b
9000加密后的结果是44b2395c46

2.5.3 while 从文件读取添加用户

# for是按照空格作为分隔符读取
# while是按照行来读取的文件

while read line
do
	user=$(echo $line | awk -F ':' '{print $1}')
	pass=$(echo $line | awk -F ':' '{print $2}')


	id $user &>/dev/null
	if [ $? -ne 0 ];then
		useradd $user 
		echo "$pass" | passwd --stdin $user &>/dev/null
		echo "$user $pass is create ok"
	else
		echo "$user" 已经存在
	fi
	
done<user.txt

2.5.4 防止ssh暴力破解脚本

如果使用 tom 用户登录系统,则立即将其踢出,然后将其拉入黑名单,以防止该用户继续使用该IP地
址进行登录。
将 sshd:username ip地址 内容 至/etc/hosts.deny 则可拒绝用户登陆系统。

#!/usr/bin/bash

. /etc/init.d/functions

index=0
while true
do
	User=tom
	login_user=$(who | grep ${User} | wc -l)
	deny_file=/etc/hosts.deny

	if [ $login_user -gt 0 ];then
		# 说明用户是存在
		login_user_ip=$(who | grep ${User} | awk '{print $NF}' | awk -F '[()]' '{print $2}'|sort |uniq)

		# 写入限制
		echo "sshd:${User} ${login_user_ip}" >> ${deny_file}
		
		# 剔除用户
		pkill -9 -u ${User}

		action  "探测 ${User} 已经登录系统, 已被剔除" /bin/false
	else
		index=$[ $index + 1]
		action "探测 ${User} 是否登录系统, 正在进行第 ${index} 次探测,目前正常" /bin/true
		sleep 3
		continue
	fi
done

三、select循环

select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择

select var in ...
do
	...
	break
done
[root@manager select]# cat select1.sh 
#!/bin/bash
PS3="choose one:" # select默认使用PS3变量的值做提示符
select var in {A..E}
do
	echo "your chocie  is $var"
	break # 跳出select,否则是死循环
done

[root@manager select]# sh select1.sh 
1) A
2) B
3) C
4) D
5) E
choose one:3
your chocie  is C

若省略 in list 则select会把 $@ 当做列表项

[root@manager select]# cat select2.sh 
#!/bin/bash
PS3="choose one:"
select var
do
	echo "your chocie  is $var"
	break
done

[root@manager select]# sh select2.sh  香蕉 苹果 西瓜
1) 香蕉
2) 苹果
3) 西瓜
choose one:1
your chocie  is 香蕉
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值