内容目录
for循环
什么是循环
- 脚本在执行任务的时候,总会遇到需要循环执行的时候
- 场景:批量创建100个用户,我们就需要使用循环来实现
什么是for循环
- 很多人把
for
循环叫做条件循环 - 因为
for
循环的次数和给予的条件是成正比的,也就是你给5个条件,那么他就循环5次
for循环基础语法
for
循环基础语法示例
# for循环语法
for 变量名 in [取值列表]
do
循环体
done
# for循环示例(默认以空格做分割,给了三个条件)
for var in file1 file2 file3
do
echo the test is $var
done
for
循环使用示例
for i in {1..100}
do
echo "test-$i is ok"
done
for i in $(seq 100)
do
echo "test-$i is ok"
done
for i in $(cat /etc/hosts)
do
echo $i
done
# c语言风格:
for ((i=1;i<=10;i++))
do
echo $i
done
for
默认循环使用空格为分隔符,可以使用IFS
来自定义分隔符- 以冒号做分隔符:
IFS=:
- 以换行做为字段分隔符:
IFS=$'\n'
- 以冒号做分隔符:
IFS=$'\n'
for i in $(cat /etc/hosts)
do
echo $i
sleep 0.5
done
for循环通过文件创建用户脚本
- 通过读入文件中的用户与密码字段,进行批量添加用户
- 文件中的格式:
user:passwd
- 分析:循环
- 1.先读入文件
- 2.进行字段分割
- 3.完成创建
cat user-file-pass.sh
#!/usr/bin/bash
. /etc/init.d/functions
for i in $(cat user.txt)
do
user=$(echo $i | awk -F ":" '{print $1}')
passwd=$(echo $i | awk -F ":" '{print $2}')
id $user &> /dev/null
if [ $? -ne 0 ];then
useradd $user
echo "$passwd" | passwd --stdin $user &> /dev/null
sleep 1
action "$user is create ok..." /bin/true
else
sleep 1
action "$user already exists..." /bin/false
fi
done
for循环输出整数升序降序脚本
- 需求:同时输出1-9的升序和降序
cat for1-9.sh
#!/usr/bin/bash
a=0
b=10
for i in {1..9}
do
#let a++;
#let b--;
a=$[ $a + 1 ]
b=$[ $b - 1 ]
echo "number is $a $b"
done
for循环计算100以内整除3脚本
需求:100以内能整除3的数值求和脚本
借助:取余数
i % 3 结果为0,说明这个数值是可以除以3
cat sum.sh
#!/usr/bin/bash
sum=0
for i in {1..100}
do
num=$[ $i % 3 ]
if [ $num -eq 0 ];then
sum=$[ $sum + $i ]
fi
done
echo $sum
for循环计算1+2+3+n的值的脚本
- 计算
1+2+3+...+n
求和,如n=8769
echo {1..8769} | sed 's# #+#g' | bc
38452065
cat sumn.sh
#!/usr/bin/bash
num=0
for i in {1..8769}
do
num=$[ i + $num]
done
echo $num
for循环案例
探测主机存活性脚本
- 需求:批量探测某个网段的主机存活状态
- 1.通过
for
循环遍历出所有的ip
地址 - 2.将所有
ip
地址写入到一个文本文件中
- 1.通过
cat ip.sh
#!/usr/bin/bash
rm -f ip_ok.txt
for i in {1..254}
do
{
ip=10.1.1.$i
ping -c1 -W1 $ip &> /dev/null
if [ $? -eq 0 ];then
echo "$ip ping ok" >> ip_ok.txt
echo "$ip ping ok"
fi
}&
done
wait
sh ip.sh | sort -t . -k4 -n
192.168.99.1 ping ok
192.168.99.3 ping ok
192.168.99.4 ping ok
192.168.99.5 ping ok
192.168.99.6 ping ok
192.168.99.7 ping ok
192.168.99.10 ping ok
192.168.99.11 ping ok
192.168.99.12 ping ok
192.168.99.13 ping ok
192.168.99.21 ping ok
192.168.99.31 ping ok
seq 254 | sed -r 's#(.*)#110.1.1.\1#g' > ip_new.txt
cat ip_new.sh
#!/usr/bin/bash
rm -r ip_ok.txt
for ip in $(cat ip_new.txt)
do
{
ping -c1 -W1 $ip &> /dev/null
if [ $? -eq 0 ];then
echo "$ip ping ok" >> ip_ok.txt
echo "$ip ping ok"
fi
}&
done
wait
echo "done..."
- 需求:批量探测某个网段的主机存活状态;要求判断三次,如果三次失败则失败;
- 判断是否存活
- 存活,则直接输出
- 不存活,再探测三次
cat ip3.sh
#!/usr/bin/bash
for i in {1..254}
do
ip=189.6.254.$i
ping -c1 -W1 $ip &> /dev/null
if [ $? -ne 0 ];then
#内循环
for j in {1..3}
do
ping -c1 -W1 $ip &> /dev/null
if [ $? -eq 0 ];then
echo "$ip ping了第 $[$j+1] 次才通..."
break
else
echo "$ip ping了 $[$j+1] 次都不通..."
fi
done
else
echo "$ip ping ok..."
fi
done
探测主机开放端口脚本
- 需求:
- 1.有一个
ip_server.txt
的文件,里面有很多ip
地址 - 2.有一个
port.txt
的文件,里面有很多端口号 - 3.现在希望对
ip_server.txt
的每个ip
地址进行port.txt
文件中的端口号进行挨个探测 - 4.最后将开放的端口和
ip
保存到一个portok.txt
文件
- 1.有一个
cat scanport.sh
#!/usr/bin/bash
for ip in $(cat ip_server.txt)
do
for port in $(cat port.txt)
do
nc -z -w 1 $ip $port &> /dev/null
if [ $? -eq 0 ];then
echo "$ip $port is ok"
fi
done
done
cat scanport.sh
#!/usr/bin/bash
for ip in $(cat ip_server.txt)
do
for port in $(cat port.txt)
do
nc -z -w 1 $ip $port &> /dev/null1
if [ $? -eq 0 ];then
if [ $port -eq 80 ];then
echo -e "\e[31m$ip $port is ok,比较危险...\e[0m"
else
echo -e "\e[32m$ip $port is ok\e[0m"
fi
fi
done
done
获取系统普通用户脚本
- 需求:获取系统的所有用户并输出。
cat user.sh
#!/usr/bin/bash
index=1
for user in $(awk -F ':' '{print $1}' /etc/passwd)
do
echo "This is $index user:$user"
#let index++
index=$[ $index + 1 ]
done
- 效果如下:
sh user.sh
This is 1 user:root
This is 2 user:bin
This is 3 user:daemon
This is 4 user:adm
This is 5 user:lp
This is 6 user:sync
This is 7 user:shutdown
This is 8 user:halt
This is 9 user:mail
This is 10 user:operator
This is 11 user:games
This is 12 user:ftp
This is 13 user:nobody
This is 14 user:systemd-network
This is 15 user:dbus
This is 16 user:polkitd
This is 17 user:sshd
This is 18 user:postfix
This is 19 user:chrony
This is 20 user:zabbix
This is 21 user:apache
This is 22 user:nginx
This is 23 user:mysql
This is 24 user:snmptt
This is 25 user:ntp
This is 26 user:tcpdump
实现九九乘法表脚本
cat 99.sh
#!/usr/bin/bash
for i in {1..9}
do
for j in {1..9}
do
echo -n "$i * $j = $[ $i *$j ] "
if [ $i = $j ];then
echo ""
break
fi
done
done
- 效果如下:
sh 99.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
对mysql数据库进行备份
场景1:备份mysql
数据库,将每个库都备份为一个sql
文件,
存储至/backup/mysql/yyyy-mm-dd/xx.sql
;
1.拿到库名称
mysql -uroot -pxxx -e "show databases;" | sed 1d | egrep -v "*_schema"
mysql
test
wordpress
2.根据库名称拼接备份命令
mysqldump -uroot -pxxx -B wordpress > /tmp/wordpress.sql
cat mysql_database_backup.sh
#!/usr/bin/bash
. /etc/init.d/functions
Db_Name=$(mysql -uroot -pxxx -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 -pxxx -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/false
fi
done
执行结果如下:
sh mysql_database_backup.sh
/backup/mysql/2024-07-28/mysql.sql 备份成功 [ 确定 ]
/backup/mysql/2024-07-28/test.sql 备份成功 [ 确定 ]
/backup/mysql/2024-07-28/wordpress.sql 备份成功 [ 确定 ]
ll -h /backup/mysql/2024-07-28/
总用量 22M
-rw-r--r-- 1 root root 503K 7月 28 09:59 mysql.sql
-rw-r--r-- 1 root root 1.4K 7月 28 09:59 test.sql
-rw-r--r-- 1 root root 21M 7月 28 09:59 wordpress.sql
场景2:对mysql
数据库进行分库分表备份,
存储至/backup/mysql/yyyy-mm-dd/database/tables.sql
;
#!/usr/bin/bash
. /etc/init.d/functions
Db_Name=$(mysql -uroot -pxxx -e "show databases;" | sed 1d | egrep -v "*_schema")
Date=$(date +%F)
Db_Dir=/backup/mysql/${Date}
. /etc/init.d/functions
# 外循环,提取库的名称
for database in ${Db_Name}
do
# 创建库对应的备份目录
if [ ! -d $Db_Dir/$database ];then
mkdir -p "$Db_Dir/$database"
fi
# 提取表名称
Tb_Name=$(mysql -uroot -pxxx -e "use ${database};show tables;" | sed 1d)
# 内循环,基于库名称然后获取表名称
for table in ${Tb_Name}
do
mysqldump -uroot -pxxx ${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
while循环
什么是while
while
在shell
中也是负责循环的语句,和for
一样
while与for如何选
- 因为功能一样,很多人在学习和工作中的脚本遇到循环到底该使用
for
还是while
呢 - 很多人不知道,就会出现有人一遇循环就使用
for
,有人一遇循环就使用while
- 到底选
for
好还是while
好:- 1.知道循环次数的使用
for
,比如一天循环24次 - 2.如果不知道要循环多少次,那就用
while
,比如猜数字游戏,每个人猜对的次数是未知的
- 1.知道循环次数的使用
while循环基础语法
- 1.
while
循环语法
# 当条件测试成立(条件测试为真),执行循环体 if [];then
while 条件测试
do
循环体
done
- 2.
while
循环基本使用示例,降序输出10到1的数字
cat while-1.sh
#!/usr/bin/bash
var=10
while [ $var -gt 0 ]
do
echo $var
var=$[ $var - 1 ]
done
- 3.
while
循环基本使用示例,输出如下图,两数相乘
9 * 9 = 81
8 * 8 = 64
7 * 7 = 49
6 * 6 = 36
5 * 5 = 25
4 * 4 = 16
3 * 3 = 9
2 * 2 = 4
1 * 1 = 1
cat while-2.sh
#!/usr/bin/bash
var=9
while [ $var -gt 0 ]
do
echo "$var * $var" = $[ $var * $var ]
var=$[ $var - 1 ]
done
while嵌套整数比对
- 循环嵌套整数比对,判断用户输入的数值是否大于0,如果大于0,则三秒输出一次大于。
cat while_number.sh
#!/usr/bin/bash
read -p "请输入数字:" num
while [ $num -gt 0 ]
do
echo "大于"
sleep 3
done
while嵌套文件比对
- 循环嵌套文件比较,判断
/tmp/hello
目录是否存在- 如果不存在则
3s
输出一次not found
- 如果存在自动退出
- 如果不存在则
cat while_dir.sh
#!/usr/bin/bash
while [ ! -d /tmp/hello ]
do
echo "not found /tmp/hello"
sleep 3
done
while嵌套字符比对
- 循环嵌套字符比较,判断用户输入的用户名,如果不是
root
则一直让其输入
cat while_string.sh
#!/usr/bin/bash
read -p "Login: " account
while [ $account != 'root' ]
do
read -p "Login: " account
done
while循环控制语句
- 在使用循环语句进行循环的过程中,有时候需要在未达到循环结束时强制跳出循环
- 那么
shell
给我们提供了内置方法来实现该功能:exit、break、continue
exit方法
exit
,退出整个程序- 当脚本碰到
exit
时,直接退出,剩余不管有多少代码都不执行
cat for_exit.sh
#!/usr/bin/bash
for i in {1..3}
do
echo "123"
exit
echo "456"
done
echo "Done..."
# 执行后的结果
sh for_exit.sh
123
break方法
break
,结束当前循环- 当脚本碰到
break
时,会结束当前循环,但会执行循环之后的所有代码
cat for_break.sh
#!/usr/bin/bash
for i in {1..3}
do
echo "123"
break
echo "456"
done
echo "Done..."
# 执行后的结果
sh for_break.sh
123
Done...
continue方法
continue
忽略本次循环剩余的所有代码- 当脚本碰到
continue
时,直接进入下一次循环,直到循环结束,然后继续执行循环之后的代码
cat for_continue.sh
#!/usr/bin/bash
for i in {1..3}
do
echo "123"
continue
echo "456"
done
echo "Done..."
# 执行后的结果
sh for_continue.sh
123
123
123
Done...
for嵌套continue
- 需求:循环嵌套
continue
,打印1-9,当数值为5则跳过本次循环,继续下一次循环。
cat continue.sh
#!/usr/bin/bash
for i in {1..9}
do
if [ $i -eq 5 ];then
continue
fi
echo $i
done
# 执行结果如下
sh continue.sh
1
2
3
4
6
7
8
9
for嵌套break
- 需求:循环嵌套
break
,打印1-9,当数值为5则停止。
cat break.sh
#!/usr/bin/bash
for i in {1..9}
do
if [ $i -eq 5 ];then
break
fi
echo $i
done
# 执行结果如下:
sh break.sh
1
2
3
4
while循环案例
猜测随机数字脚本
- 猜数字游戏
- 1.随机输出一个1-100的数字
- 2.要求用户输入的必须时数字
- 3.如果比随机数小则提示比随机数小,大则提示比随机数大了
- 4.正确则退出,错误则继续死循环
- 5.最后统计猜了多少次才对
cat random.sh
#!/usr/bin/bash
sj=$(echo $[ $RANDOM%100+1 ])
index=0
while true
do
read -p "请输入数字(1-100):" Action
if [[ ! $Action =~ ^[0-9]+$ ]];then
continue
fi
# 计数
index=$[ $index+1 ]
if [ $Action -eq ${sj} ];then
echo "恭喜你,你花了 ${index} 次猜对了"
exit
fi
if [ $Action -gt ${sj} ];then
echo "你输入的数字太大..."
else
echo "你输入的数字太小..."
fi
done
破解随机字符串脚本
- 字符串
efbaf275cd、4be9c40b8b、44b2395c46
- 是通过对随机数变量
RANDOM
速记执行命令:echo $RANDOM | md5sum | cut -c1-10
后的结果 - 请破解这些字符串对应的
RANDOM
值
cat while_random.sh
#!/usr/bin/bash
for i in efbaf275cd 4be9c40b8b 44b2395c46
do
num=0
while true
do
md5_sum=$(echo ${num} | md5sum | cut -c1-10)
if [ $md5_sum == $i ];then
echo "$num 加密后的结果是 $i"
break
else
num=$[ $num + 1 ]
continue
fi
done
done
# 执行的结果如下:
sh while_random.sh
15000 加密后的结果是 efbaf275cd
12000 加密后的结果是 4be9c40b8b
9000 加密后的结果是 44b2395c46
通过文本创建用户脚本
- 通过读入文件中的用户与密码文件,进行批量添加用户
- 文件中的格式:
user:passwd
cat user.txt
hello:hl
sunday:sd
root:root
cat while_useradd.sh
#!/usr/bin/bash
. /etc/init.d/functions
while read line
do
user=$(echo $line | awk -F : '{print $1}')
pass=$(echo $line | awk -F : '{print $2}')
id $user &> /dev/null
if [ $? -eq 0 ];then
action "useradd $user is already exists" /bin/false
else
useradd $user
echo "$pass" | passwd --stdin $user &> /dev/null
action "useradd $user is created and passwd is ok." /bin/true
fi
done <user.txt
# 执行结果如下:
sh while_useradd.sh
useradd hello is created and passwd is ok. [ 确定 ]
useradd sunday is created and passwd is ok. [ 确定 ]
useradd root is already exists [失败]
防止ssh暴力破解脚本
- 如果使用
sunday
用户登录系统,则立即将其踢出,然后将其拉入黑名单,以防止该用户继续使用该IP
地址进行登录 - 将
sshd:username ip
地址写入/etc/hosts.deny
则可拒绝用户登录系统
cat deny_user.sh
#!/usr/bin/bash
while true
do
# 获取用户的名称
deny_user=$(who | grep sunday | awk '{print $1}' | uniq)
# 获取用户的来源ip
deny_ip=$(who | grep sunday | awk '{print $5}' | awk -F '[()]' '{print $2}' | uniq)
# 判断用户变量是否为空,不为空说明该用户已登录系统
if [ ! -z $deny_user ];then
pkill -9 -U sunday
echo "${deny_user} user is killed"
# 将用户加入黑名单,防止再次登录系统
echo "sshd:${deny_user} ${deny_ip}" >> /etc/hosts.deny
fi
sleep 5
done
# 执行结果如下:
sh deny_user.sh
sunday user is killed
sunday user is killed
# 远程登录主机结果如下:
Connection to x.x.x.x closed by remote host.
Connection to x.x.x.x closed.
ssh sunday@x.x.x.x
ssh_exchange_identification: read: Connection reset by peer
cat /etc/hosts.deny
sshd:sunday x.x.x.x
sshd:sunday y.y.y.y
留言