shell循环

循环的分类

循环名称 循环含义 使用场景
while循环 当型循环 死循环/有条件的循环/一行一行的读取文件
do until循环 直到型循环,一直循环直到不满足条件 没什么场景,因为有for循环
for循环 通用型 除了while循环的三个场景外都可以使用

循环语法

while循环

while <条件表达式>;do
	cmd1
	cmd2
	cmd3
done

while <条件表达式>
do
	cmd1
	cmd2
	cmd3
done

### 死循环
while true;do
  echo 'hei hei hei'
done

while [ 1 -eq 1 ];do 
  echo 'hei hei hei'
done

while :;do
  echo 'hei hei hei'
done

使用while循环模拟for循环seq

[root@web02 ~]# vim a.sh
#!/bin/bash

n=1
while [ $n -le 10 ];do
  echo $n
  ((n=n+1))
done

[root@web02 ~]# vim a.sh
#!/bin/bash

for n in `seq 10`;do
echo $n
done

使用while循环实现1加到100

[root@web02 ~]# vim a.sh

#!/bin/bash

i=1
num=0
while [ $i -le 100 ];do
  ((num=num+i))
  ((i++))
done
echo $num


[root@web02 ~]# echo {1..100}|tr ' ' '+'|bc
5050
[root@web02 ~]# seq -s+ 100|bc
5050
[root@web02 ~]# seq 100|awk '{m=m+$1}END{print m}'
5050
[root@web02 ~]# awk 'BEGIN{for(i=1;i<=100;i++)sum=sum+i;print sum}'
5050

for循环

语法一

无法指定循环次数,变量表达式中有多少内容就循环多少次

[root@web02 ~]# vim a.sh
#!/bin/bash

for var in wsh www fff;do
  echo $var
done

变量表达式:
	- 可以是文件中的内容(按空格循环)
	- 可以是序列命令的内容
	- 可以是数组中的内容
	- 可以是以空格为分隔符的字符串
	
## 文件内容
[root@web02 ~]# vim a.sh

#!/bin/bash

for var in `cat 2.txt`;do
  echo $var
done
[root@web02 ~]# sh a.sh 
111
222
333

[root@web02 ~]# vim a.sh
#!/bin/bash

for var in $(cat 2.txt);do
  echo $var
done

## 序列内容
[root@web02 ~]# vim a.sh

#!/bin/bash

for var in `seq 10`;do
  echo $var
done

[root@web02 ~]# vim a.sh
#!/bin/bash
array=(www fff ggg)
for var in ${array[*]};do
  echo $var
done

[root@web02 ~]# vim a.sh
#!/bin/bash
name="www fff ggg"
for var in $name;do
  echo $var
done

语法二

指定循环次数
[root@web02 ~]# vim a.sh
#!/bin/bash
for ((i=1;i<=10;i++));do
  echo $i
done
死循环
[root@web02 ~]# vim a.sh
#!/bin/bash
for (( ; ; ));do
  echo 123
done

循环的控制语句

continue

break

break跳出循环

break命令允许跳出所有循环(终止执行后面的所有循环)。

下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。

[root@web02 ~]# vim a.sh
#!/bin/bash
while true;do
  read -p 'please input a number:' num
  if [ $num -ne 5 ];then
    echo "你输入的是 $num"
  else
    break
  fi
done

[root@web02 ~]# vim a.sh
#!/bin/bash
n=0
while [ $n -lt 10 ];do
        ((n++))
        if [ $n -eq 5 ];then
                break
        fi
        echo $n
done

continue 跳出循环

continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

[root@web02 ~]# vim a.sh
#!/bin/bash
while true;do
  read -p 'Please Input A Number: ' num
  if [ $num -ne 5 ];then
      echo "你输入的是 $num"
  else
      continue
  fi
done

[root@web02 ~]# vim a.sh
#!/bin/bash
n=0
while [ $n -lt 10 ];do
        ((n++))
        if [ $n -eq 5 ];then
                continue
        fi
        echo $n
done

while循环读取文件

## for循环读取文件
[root@web02 ~]# cat 2.txt 
111
222
333

[root@web02 ~]# vim a.sh
#!/bin/bash
for word in `cat 2.txt`;do
  echo $word
done

## while读取文件
# 方法一:
[root@web02 ~]# vim a.sh
#!/bin/bash
exec < 2.txt
while read line;do
  echo $line
done

# 方法二:
[root@web02 ~]# vim a.sh
#!/bin/bash
cat 2.txt|while read line;do
  echo $line
done

# 方法三:
[root@web02 ~]# vim a.sh
#!/bin/bash
while read line;do
  echo $line
done < 2.txt

while练习

写一个脚本,读取下面文件内容,并算出所有人年龄总和
[root@web02 ~]# vim 1.txt 
张三 18
李四 20
王五 33
赵六 32

## 方法一
[root@web02 ~]# vim a.sh 
#!/bin/bash
sum=0
while read line;do
  age=`echo $line | awk '{print $2}'`
  ((sum+=age))
done < 1.txt
echo $sum

## 方法二
[root@web02 ~]# vim a.sh 
#!/bin/bash
sum=0
exec < 1.txt
while read line;do
  age=`echo $line | awk '{print $2}'`
  ((sum+=age))
done
echo $sum

案例

## 根据菜单,安装对应的架构
输入数字和 lnmp  nginx

[root@m01 scripts]# cat install.sh 
#!/bin/bash

cat <<EOF
+---------+
| 1.lnmp  |
+---------+
| 2.lnmt  |
+---------+
| 3.lamp  |
+---------+
| 4.lamt  |
+---------+
| 5.nginx |
+---------+
| 6.apache|
+---------+
| 7.tomcat|
+---------+
| 8.php   |
+---------+
| 9.MySQL |
+---------+
EOF

check_yum(){
	service=$1
	service_count=`rpm -qa|grep -w $service|wc -l`
	if [ $service_count -gt 0 ];then
		echo "$service 已安装"
		return 1
	fi
}

check_source(){
	service=$1
	path=$2
	if [ -e $path ];then
		echo "$service 已安装"
		return 1
	fi
}

install_yum_pkg(){
	service=$1
	check_yum $service
	if [ $? -ne 1 ];then
		echo "正在安装 $service ..."
		yum install -y $service &>/dev/null
		check_yum $service
		if [ $? -ne 1 ];then
			echo "$service 安装失败..."
		fi
	fi
}

install_source_pkg(){
	service1=$1
	path=$2
	install_path=$3
	check_source $service1 $path
	if [ $? -ne 1 ];then
		if [ ! -d $install_path ];then
			mkdir -p $install_path
		fi
		echo "正在安装 $service1 依赖包..."
		install_yum_pkg openssl-devel
		install_yum_pkg pcre-devel

                echo "正在解压 $serivce1 ..."
                tar xf nginx-1.22.0.tar.gz

                echo "正编译安装 $service1 ..."
                cd nginx-1.22.0 && \
		./configure --prefix=$install_path/nginx-1.22.0 --with-http_ssl_module &>/dev/null && \
		make &>/dev/null && \
		make install &>/dev/null

		echo "正在创建 $service1 软连接..."
		ln -s $install_path/nginx-1.22.0 $install_path/nginx

		echo "正在添加环境变量..."
		echo 'PATH="/$installl_path/nginx/sbin:$PATH"' > /etc/profile.d/nginx.sh
		install_source_pkg $service1 $path
		if [ $? -ne 1 ];then
			echo "$service1 安装失败..."
		fi
	fi
}

read -p '请输入编号或服务名:' num

if [ $num ==  '6' -o $num == 'apache' ];then
	install_yum_pkg httpd
elif [ $num ==  '9' -o $num == 'mysql' ];then
	install_yum_pkg mariadb
elif [ $num ==  '5' -o $num == 'nginx' ];then
	install_source_pkg nginx /app/nginx /app
fi

案例一

日志分析案例

写一个shell脚本,防止DDOS攻击,先分析日志,监控某一个IP并发连接数,若短时内PV达到100阈值,则调用防火墙命令,封掉该IP。

可以分析nginx日志或者查看当前网络连接数 ss -ant 或者 netstat -ant

这里使用一个生产的日志 netstat.log

[root@web02 ~]# vim a.sh

#!/bin/bash
awk -F '[ :]+' '/^tcp/{print $6}' netstat.log|grep -E '^[1-9]'|sort|uniq -c|sort -nr|while read line_ip;do
        exception_ip_count=`echo $line_ip|awk '{print $1}'`
        exception_ip=`echo $line_ip|awk '{print $2}'`

        if [ $exception_ip_count -gt 5 ];then
                firewall-cmd --add-source=$exception_ip --zone=drop &>/dev/null
                echo "$exception_ip 访问异常,添加到防火墙..."
        fi
done

案例二

在指定目录下,通过随机10个小写字母,然后生成一个文件名为:随机字母_zls.txt的文件

生成随机内容的方法
## 方法一:
[root@zabbix01 ~]# openssl rand -base64 10
UUBtKUvUkEfcEw==

## 方法二:
[root@zabbix01 ~]# echo $((RANDOM))|md5sum

## 方法三:
[root@zabbix01 ~]# date +%N|md5sum
3fd3a5c57def5f89481e1961fceb13e8

## 方法四:
tr -cd 'a-zA-Z0-9'</dev/urandom|head -c 10


#!/bin/bash
num=`openssl rand -base64 10`
touch ${num}_zls.txt   

案例三

批量修改文件名

# 提示一:变量子串方法
 
# 提示二:sed awk方法
 
# 提示三:命令拼接
 
# 提示四:rename
rename zls.txt cls.TXT /root/*.txt


#!/bin/bash

for name in `ls -1 /abc`;do
        #mv /abc/${name%.*}.yml /abc/${name%.*}.j2
        mv /abc/$name /abc/${name//j2/yml}
done

案例四

创建100个系统用户,生成随机密码

并将用户名和密码保存到文件中

用户名:密码

[root@web02 ~]# vim a.sh 
#!/bin/bash

read -p "请输入需要创建的用户名: " user_name
user_list=`echo ${user_name}{1..100}`

for name in $user_list;do
        id $name &>/dev/null
        if [ $? -ne 0 ];then
                useradd $name &>/dev/null
                pass=`echo $RANDOM|md5sum|cut -c 1-10`
                echo $pass |passwd --stdin $name &>/dev/null
                echo $name:$pass >> /tmp/user.log
        else
                echo "$user_list 已存在"
                break
        fi
done

案例五

现在我们要模拟黑客,来扫描,指定网段内存活的主机

for n in `seq 255`;do
  ping 10.0.0.$n
done



## 优化后的
#!/bin/bash

. /etc/init.d/functions
#for n in `seq 254`;do
#  ping -c 1 10.0.0.$n
#done

for n in `seq 254`;do
{
  ping -c1 -W1 -i1 10.0.0.$n &>/dev/null
  if [ $? -eq 0 ];then
    action 10.0.0.$n /bin/true
    for ;do
    {
        xxx
    } &
    usleep 300
    done
  fi
} &
usleep 300
done

#### 扫描存活的主机后,在存活的主机上,扫描都开了哪些端口?
#!/bin/bash

. /etc/init.d/functions

for n in `seq 3 254`;do
  ping -c1 -W1 -i1 10.0.0.$n &>/dev/null
  if [ $? -eq 0 ];then
    action 10.0.0.$n /bin/true
    for port in `seq 65535`;do
        {
        res=`echo ''|telnet 10.0.0.$n $port 2>/dev/null|grep 'Connected'|wc -l`
        if [ $res -ne 0 ];then
                action "10.0.0.$n 主机端口: $port" /bin/true
        fi
        } &
        usleep 500
    done
  fi
done

figlet

[root@web02 ~]# figlet wsh
              _     
__      _____| |__  
\ \ /\ / / __| '_ \ 
 \ V  V /\__ \ | | |
  \_/\_/ |___/_| |_|

[root@web02 ~]# showfigfonts

[root@web02 ~]# figlet wsh -f banner
                     
#    #  ####  #    # 
#    # #      #    # 
#    #  ####  ###### 
# ## #      # #    # 
##  ## #    # #    # 
#    #  ####  #    #

[root@web02 ~]# watch -n1 "date +%D%n%T|figlet -k"