内容目录

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地址写入到一个文本文件中
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文件
 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

  • whileshell中也是负责循环的语句,和for一样

while与for如何选

  • 因为功能一样,很多人在学习和工作中的脚本遇到循环到底该使用for还是while
  • 很多人不知道,就会出现有人一遇循环就使用for,有人一遇循环就使用while
  • 到底选for好还是while好:
    • 1.知道循环次数的使用for,比如一天循环24次
    • 2.如果不知道要循环多少次,那就用while,比如猜数字游戏,每个人猜对的次数是未知的

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
最后修改日期: 2024年10月7日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。