shell编程(三)
一、循环
1.循环概述
循环类型 | 格式 | 说明 |
---|---|---|
for循环 | for 变量 in 清单(列表 ) ;do 命令 ;done for((i=1;i<=10;i++)) ;do echo $i ;done |
最常用的循环 |
while循环 当型循环 | while 条件 ;do 命令 ;done | while可以加入条件,死循环,读取文件 |
do until 循环 直到循环 | until 条件 ;do 命令;done | 极少用 |
2. for 循环
2.1 最常用的for循环格式
最常用的一种
for 变量 in 候补清单(列表)
do
命令
done
2.2 c语言格式for循环
for((i=1;i<=10;i++))
do
echo $i
done
i=1 变量i初始化是1.
i<=10 i的值小于等于10的时候运行循环语句。(条件)
每次循环后执行i i=i+1
1.执行循环
2.i初始值是1
3.进行判断i<=10,满足条件,运行循环。
4.运行命令,运行完成后。
5.执行i,然后进行下次循环。
2.3 for循环格式及应用场景
for循环格式 | 格式 | 应用场景 |
---|---|---|
通用格式 | for n in {1..10} do echo $n done |
大部分场景可用 |
C语言格式 | for((i=1;i<=10;i++)) do echo $i done |
循环数组可用 |
2.4案例 面试题: 使用for循环在/tmp目录下通过随机小写10 个字母加固定字符串wh批量创建10个html文件
[root@m01 /server/scripts/devops-shell]# cat for_shuiji.sh
#!/bin/bash
##############################################################
# File Name:for_shuiji.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
for (( i=1;i<=10;i++ ))do
num=`mkpasswd -l 10 -s 0`
touch /tmp/${num}_wh.html
done
补充:
生成随机字符
1. uuidgen 2. mkpasswd -l 10 -d 0 -s 0 -C 0 -l 密码长度 -d 数字数量 -s special 特殊字符 -C 大写字母 -c 小写字母 3. tr tr -cd 'a-z' dev/urandom |head -c10 -c取反, -d删除 /dev/urandom 字符设备,生成随机字符。 tr -cd 'a-zA-Z0-9' dev/urandom 4. date +%N |md5sum %N纳秒 5. echo $RANDOM 0-32767 echo $((RANDOM+10000000)) echo $((RANDOM+10000000)) | md5sum
3. while 循环
3.1 while循环通用格式
while 条件
do
命令
done
温馨提示:
while循环只会在满足条件后运行。
3.2案例 输出1+2+3+4+5++100的结果
[root@m01 /server/scripts/devops-shell]# cat while.sh
#!/bin/bash
##############################################################
# File Name:while.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
i=1
sum=0
while [ $i -le 100 ]
do
let sum=sum+$i
let i++
done
echo $sum
3.3 while循环-死循环
含义:一直循环,直到用户手动中止或满足指定条件后退出。
应用场景: 一般与用户交互的时候或脚本在系统后台持续运行。
while true
do
echo test
done
#while true中的true本质是一个命令,命令永远成功。
还可以写成
while :
do
echo test
done
#: 相对是一个命令,没啥用,仅仅用于返回值是0.
3.4 案例 生成随机数字,判断数字是什么(1-100)
如果输入的数字比随机数大,提示大了,
如果输入数字比随机数小,提示小了,
如果等于提示恭喜
额外要求:
用了1-3次 超越了99.99%人
用了4-6次 超越80%的人
其他 超越了70%的人
[root@m01 /server/scripts/devops-shell]# cat randnum.sh
#!/bin/bash
##############################################################
# File Name:randnum.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
randnum=`echo $((RANDOM%100))`
i=0
check_percent(){
if [ $i -ge 1 -a $i -le 3 ];then
echo "您超越了99.99%的人"
elif [ $i -ge 4 -a $i -le 6 ];then
echo "您超越了80%的人"
else
echo "您超越了70%的人"
fi
}
while :
do
read -p "请输入数字:" num
let i++
[[ "$num" =~ ^[0-9]+$ ]] || {
echo "输错了,请重新输入"
continue
}
if [ $num -lt $randnum ];then
echo "$num这个数字小了"
elif [ $num -gt $randnum ];then
echo "$num这个数字大了"
else
echo "恭喜您猜对了"
echo "您一共猜了${i}次"
check_percent
exit 1
fi
done
3.5 while循环-读取文件内容
应用场景: 需要在脚本中读取文件内容,多行。
此时可以选择3剑客或while循环.
#方式1:采用exec读取文件后,然后进入while循环
处理。
exec<FILE
while read line
do
cmd
echo $line
done
#方式2:使用cat读取文件内容,然后通过管道进入 不适用于有变量传
递场景使用。
while循环处理。
cat FILE|while read line
do
cmd
echo $line
done
#输入重定向指定读取的文件。 推荐使用.
while read line
do
cmd
done<FILE
3.6案例 通过while read方式,统计num.txt文件行数
#采用exec读取文件后,然后进入while循环
[root@m01 /server/scripts/devops-shell]# seq 100 > num.txt
[root@m01 /server/scripts/devops-shell]# cat while_file.sh
#!/bin/bash
##############################################################
# File Name:while_file.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
file=num.txt
i=0
while read line
do
echo $line
let sum=sum+line
let i++
done < $file
echo "文件总共$i 行"
echo "文件总和是 $sum"
补充:
while读取文件的方法2 vs 方法3 区别
#方法2 i=0 j=0 #01 echo "方法2:while + cat" cat $file |while read ip do echo $ip let i done echo "次数 $i" #方法3 echo "方法3:while + 输入" while read ipaddr do echo $ipaddr let j done<$file echo "次数 $j" #结果 方法2:while + cat 10.0.0.5 10.0.0.5 10.0.0.5 10.0.0.5 10.0.0.5 次数 0 方法3:while + 输入 10.0.0.5 10.0.0.5 10.0.0.5 10.0.0.5 10.0.0.5 次数 5 方法2在运行的时候因为管道,创建1个子shell,变量都存放在子shell中,子shell运行完成,消失了,变量也没了。 方法3运行的时候是与当前脚本在同一个shell中,所以变量都保持了,可以继续使用。
3.7 案例:分析ngx访问日志找出访问量最高前5个ip及他们的 访问次数,IP访问次数大于200,通过防火墙屏蔽ip
防DOS,拒绝式服务攻击.
DDOS分布式拒绝式服务攻击.
CC 基于http请求攻击
[root@m01 /server/scripts/file]# cat grep_ip.sh
#!/bin/bash
##############################################################
# File Name:grep_ip.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
access_file=/server/scripts/file/access.log
result_file=/server/scripts/file/result.txt
awk '{print $1}' $access_file |sort|uniq -c|sort -rn|head -5 >$result_file
while read line
do
ip_count=`echo $line|awk '{print $1}'`
ip=`echo $line|awk '{print $2}'`
iptables_ip=`iptables -nL |grep -wc ${ip} |wc -l`
echo "${ip_count}"
echo "${ip}"
echo "${iptables_ip}"
if [ ${ip_count} -gt 200 -a ${iptables_ip} -eq 0 ];then
iptables -t filter -I INPUT -s ${ip} -j DROP
fi
done < ${result_file}
温馨提示:
文件的每一行的 第1列 赋值给count变量 文件的每一行的 第2列 赋值给ip变量 while read count ip do echo "ip地址是$ip 访问次数是$count" done<$result_file
4. do-until循环
#直到型循环: 一直循环,直到条件不满足.
until 条件
do
命令
命令
....
done
until 话费是否充足
do
发短信
done
5. 循环控制语句
5.1概述
语句 | 含义 | 应用场景 |
---|---|---|
exit | 终止执行脚本,退出返回值. exit n n 0-255 | 基础用法,判断后加上exit 脚本结束加上exit 用于脚本中检查部分 |
return | 放在函数中,终止执行函数,函数返回 值 | 写在函数中,检查函数命令运行是否成功。方便调试. |
break | 终止循环(退出),不会继续运行剩余循 环 | 要在循环中表示退出循环。 |
continue | 终止本次循环,进入下一次循环 跳过 | 要在循环中跳过某一次循环。 |
[root@m01 /server/scripts/devops-shell]# for n in {1..10}
> do
> [ $n -eq 5 ]&&continue
> echo $n
> done
1
2
3
4
6
7
8
9
10
二、辅助功能
1.颜色
红色字test
echo -e "\033[31m红色字test \033[0m"
\E 或\033 表示要开启这种功能。
[1;31m
[效果;颜色m
\E[0m 颜色设置结束。
各种颜色的字-效果
1表示加粗,2正常的,5表示闪烁
创建环境变量或写入脚本开头
export RED="\E[5;31m"
export GREEN="\E[1;32m"
export BLUE="\E[1;34m"
export END="\E[0m"
永久使用/etc/profile中即可。
案例:用户自定义的函数库文件,自定义颜色.
[root@m01 /server/scripts/devops-shell]# cat diy_func.sh
#!/bin/bash
##############################################################
# File Name:diy_func.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
redecho() {
#颜色开头部分
echo -ne "\e[5;31m"
#取出要加上颜色的内容
echo -n "$@"
#颜色的结束部分
echo -e "\e[0m"
#echo -e "\e[5;31m $@ \e[0m"
}
greenecho() {
echo -ne "\e[1;32m"
echo -n "$@"
echo -e "\e[0m"
}
blueecho() {
echo -ne "\e[1;34m"
echo -n "$@"
echo -e "\e[0m"
}
颜色的说明:
2.gui
yum install -y dialog
dialog 功能 功能选项 窗口大小高 宽度
dialog textbox /etc/hostname 80 80
dialog msgbox "test" 10 30
三、数组
1.概述
数组也是一种变量.
数组可以存放多个 相关联内容 ,通过访问数组调用结果(值)
应用场景:用于存放相关的数据。
输出数组中所有值。
echo ${ip_array[@]}
echo ${ip_array[*]}
计算数组的元素的个数。
echo ${#ip_array[@]}
echo ${#ip_array[*]}
[root@m01 ~]# ip_array=(10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9)
[root@m01 ~]# echo ${ip_array[0]}
10.0.0.5
[root@m01 ~]# echo ${ip_array[*]}
10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9
[root@m01 ~]# echo ${#ip_array[*]}
5
[root@m01 ~]# for n in ${ip_array[*]}
> do
> echo $n
> done
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
[root@m01 ~]# for((i=0;i<${#ip_array[*]};i++))
> do
> echo $i ${ip_array[i]}
> done
0 10.0.0.5
1 10.0.0.6
2 10.0.0.7
3 10.0.0.8
4 10.0.0.9
2.shell数组赋值
shell数组创建(赋 值) | 具体形式 |
---|---|
批量直接赋值 | array=(ip01 ip02 ip03 ip04 ) ⭐ array=( 命令结果 ) # cat ip.txt 三剑客命令获取指定内容 |
逐个元素赋值 | array[0]=test array[1]=test1 |
read命令赋值 | read -p "输入数组中内容:" -a array 可以创建数组,空格分割即 可. |
3.案例:试编写一个Shell程序,该程序能接收用户从 键盘输入的10个整数,然后求出其总和、平均值
[root@m01 /server/scripts/devops-shell]# cat cala_num.sh
#!/bin/bash
##############################################################
# File Name:cala_num.sh
# Version:V1.0
# Author:wh
# Desc:
##############################################################
file_name=${0}.txt
#校验数字
check_num(){
if [ "${num}" = "q" ];then
echo "计算中,请稍等......"
break
elif [[ ! "$num" =~ ^[0-9]+$ ]];then
echo "输入错误,请重新输入"
continue
fi
}
#输入
input_num(){
[[ -f ${file_name} ]] && rm -f ${file_name}
while :
do
read -p "请逐行输入数字,完成后按q退出:" num
check_num
echo $num >>${file_name}
done
}
#计算
cala_num(){
num_array=(`cat ${file_name}`)
sum=0
for n in ${num_array[*]}
do
let sum=sum+$n
done
echo "所有数字之和为${sum}"
average_num=`echo ${sum}/${#num_array[*]} |bc -l`
echo "平均数为${average_num}"
}
main(){
input_num
cala_num
}
main
四、Debug全流程
1.书写习惯
- 注释
- 变量 :在脚本中尽量使用变量,变量命名规范(给变量加上说明)。
- 函数 :代码中尽可能使用函数,增加说明。 模块化.
- 返回值 : 尽可能增加函数return功能,方便后期调试。
- 参数与选项检查 : 尽可能增加exit 返回值的功能,方便后期调试。
- 书写的时候适当增加 输出 :书写代码的时候,可以多写一些echo用于在某些步骤中进行输出。
- 缩进 :代码注意缩进。
- 成对的符号提前输入好. "" [] {}
2.调试方法
-
-x 大部分时候使用:sh/bash -x显示详细的执行过程。
-
精确显示过程 脚本使用functions函数库,脚本过大,可以在脚本中使用
set -x 开始显示详细信息
代码
set +x 关闭显示详细信息
-
注释法 使用函数,注释法。注释多余的函数,排除。缩小范围定位问题.
-
输出关键变量 变量后增加echo输出变量内容。查看过程。
五、三剑客与shell
1.sed与变量
cat ip.txt
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
src=10.0.0
dst=172.16.1
sed "s#$src#$dst#g" ip.txt
2.awk
2.1 案例 过滤出/etc/passwd的第2到9行的第1列和第3 列
awk -F: 'NR>=2&&NR<=9{print $1,$3}' /etc/passwd
2.2 案例 第1列用户名等于root的最后一列(命令解释器)
awk -F: '$1=="root"{print $NF}' /etc/passwd
在awk中''中的单词如果不加""就表示变量
2.3 案例 第1列用户名等于root的最后一列(命令 解释器)。 使用变量方法
name=root
awk -F: -vn=$name '$1==n{print $NF}' /etc/passwd
2.4 案例 过滤出网卡文件中ip地址那行
awk -F= '/IPADDR/{print $2}' /etc/sysconfig/networkscripts/ifcfg-eth0
2.5awk的判断与循环
2.5.1 判断
#如果系统根分区磁盘使用率大于80,提示磁盘空间不足。
df -h|awk '$NF=="/"{ if($5>=80) print "磁盘空间不足"}'
温馨提示:
awk进行统计计算的时候,如果是数字+字符,变成字符串对比。左到右一个字符一个字符对比。
解决方案01:避免这个情况,通过awk指定分隔符,只获取数字部分即可。
解决方案02:通过让这个字符串进行运算,运算后会被转换为数字。
2.5.2 循环
awk 'BEGIN{for(i=1;i<=100;i) {sum=sum+i} print sum}'
2.6 awk数组
2.6.1 概述
awk数组与shell数组区别
awk数组: 关联数组,下标啥都行。
shell数组:普通数组,下标数字,shell中也有关联数组.。
2.6.2 使用
awk专用于数组的循环
for(n in 数组名字)
print n(数组下标),数组名字[n]
awkfor循环 数组专用循环
变量n,获取数组下标
数组名字[下标]才是对应的值
2.6.3 案例 分析文件中每个域名出现的次数
[root@m01 /server/scripts/file]# cat url.txt
http://www.yuanlinux.org/index.html
http://www.yuanlinux.org/1.html
http://post.yuanlinux.org/index.html
http://mp3.yuanlinux.org/index.html
http://www.yuanlinux.org/3.html
http://post.yuanlinux.org/2.html
[root@m01 /server/scripts/file]# awk -F '/+' '{url[$2]++}END{for(name in url) print name,url[name]}' url.txt
2.6.4 案例 统计access.log中每个ip地址的流量总数。
第1列是ip地址
第10列是流量(单位是字节)
[root@m01 /server/scripts/file]# awk '{ liu[$1]=liu[$1]+$10 }END{for(ip in liu) print ip,liu[ip]}' access.log|sort -rnk2|head
3.awk与shell语法格式对比
具体语法 | awk | shell编程 |
---|---|---|
if单分支判断 | if (条件) {命令 多个命令} |
if 条件;then 命令 fi |
if双分支判断 | if (条件) {命令} else |
if 条件;then 命令 else 命令 fi |
for循环:c语言形式 | for(i=1;i<=10;i) |
for((i=1;i<=10;i)) do 命令 done |
for循环:通用 | awk专用于数组的循环 for( n in 数组名字) print n(数组下标),数组名字[n] |
for n in 清单 do 命令 done |