shell编程五
10.循环
10.1 循环概述
循环类型 | 说明 |
for循环 | 最常用的循环,2种格式 |
while循环 当型循环 | while可以加入条件,死循环, 读取文件 |
do until 循环 直到循环 | 极少用 |
10.2 for循环
10.2.1 最常用的for循环格式
# 最常用的一种
for 变量 in 候补清单(列表)
do
命令
done
for n in 1 2 4 5 6 79 81
do
echo $n
done
第1次循环 变量n的值就是 1
第2次循环 变量n的值就是 2
第3次循环 变量n的值就是 4
第4次循环 变量n的值就是 5
.。。。。。
for n in {1..10} `seq 10` `ls /etc/`
10.2.2 c语言格式for循环
for((i=1;i<=3;i++))
do
echo $i
done
1
2
3
i=1 变量i初始化是1.(第1次循环的时候)
i<=3 i的值小于等于10的时候运行循环语句。(条件)
每次循环后执行i++ i=i+1
1.执行循环
2.i初始值是1
3.进行判断i<=3,满足条件,运行循环。
4.运行命令,运行完成后。
5.执行i++,然后进行下次循环。
6.循环3-5步骤
10.2.3 for循环格式及应用场景
for循环格式 | 格式 | 应用场景 |
⭐ 通用格式 |
for n in {1..10} do echo $n done |
通用,大部分场景可用。 |
c语言格式 |
for((i=1;i<=10;i++)) do echo $i done |
对数组循环使用。 |
10.2.4 生成随机字符
# 第1种方法
# debian/ubunt软件包叫:libstring-mkpasswd-perl 命令 mkpasswd.pl
# 红帽系列 软件包叫: expect 命令 mkpasswd
-l 密码长度
-d 数字数量
-s special 特殊字符
-C 大写字母
-c 小写字母
[root@Kylin-V10-sp3 ~]# mkpasswd -l 10 -d 0 -s 0 -C 0
elpamozizp
[root@Kylin-V10-sp3 ~]#
# 第2种方法
tr
-c取反,
-d删除
/dev/urandom 字符设备,生成随机字符。
# 生成随机数不会停,用head -c10(取前10个)
[root@Kylin-V10-sp3 ~]# tr -cd 'a-z' </dev/urandom |head -c10
kmzclqoijt[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# tr -cd 'a-zA-Z0-9' </dev/urandom |head -c10
0Jn26Y83cp[root@Kylin-V10-sp3 ~]#
# 第3种方法
uuidgen
[root@Kylin-V10-sp3 ~]# uuidgen
cb8080b6-f2ae-4f6d-a705-be4cd70d5dee
# 第4种方法
date +%N |md5sum
%N纳秒
[root@Kylin-V10-sp3 ~]# date +%N |md5sum
322cdee250bc439e704ae651e86609c5 -
# 第5种方法
$RANDOM 取值范围 0-32767
[root@Kylin-V10-sp3 ~]# echo $((RANDOM+100))
17996
[root@Kylin-V10-sp3 ~]# echo $((RANDOM+100)) | md5sum
9ee516054c3b4eaf7e7369ea37e61b84 -
[root@Kylin-V10-sp3 ~]#
10.2.5 批量创建文件
面试题: 使用for循环在root/test目录下通过随机小写10个字母加固定字符串xk批量创建10个html文件
#!/bin/bash
##############################################################
# File Name:random_file.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
dir=/root/test/
# 命令检查
check_cmd(){
if ! which mkpasswd >/dev/null 2>&1;then
echo "mkpasswd 命令不存在,请安装"
echo "debian mkpasswd.pl,红帽mkpasswd"
exit 1
fi
}
# 检查目录
check_dic(){
[ ! -d $dir ] && {
mkdir -p /root/test/
}
}
# 创建文件
touch_files(){
for i in {01..10}
do
random_str=`mkpasswd -l 10 -d 0 -s 0 -C 0`
filename=${random_str}_xk.html
touch $dir${random_str}
done
}
main(){
check_cmd
check_dic
touch_files
}
main
10.3 while循环
10.3.1 概述
- while循环(当型循环,当条件满足后才能执行循环内容)
- 加入条件(条件测试语句)
- 死循环
- 读取文件,管道内容
10.3.2 while循环通用格式
while 条件 #[]条件 命令
do
命令
done
# 温馨提示:
while循环只会在满足条件后运行。
10.3.3 输出1+2+3+4+5+...+10的结果
#!/bin/bash
##############################################################
# File Name:while_sum.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
# 1.while
i=1
sum=0
while [ $i -le 10 ]
do
let sum=sum+i
let i++
done
echo $sum
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_sum.sh
55
# 2.for
sum=0
for i in {1..10}
do
let sum=sum+i
done
echo $sum
# 3.awk
[root@Kylin-V10-sp3 ~/test]# seq 10 | awk '{i+=$1} END {print i}'
55
10.3.3 while循环-死循
- 含义:一直循环,直到用户手动中止或满足指定条件后退出。
- 应用场景:
- 一般与用户交互的时候或脚本在系统后台持续运行。
- 书写一个5秒钟运行1次的任务.
面试题: 每5秒钟检查下jd.com(网站是否可以访问)curl/wget,失败超过5次则输出警告信息(重启)
check_url_while.sh
#!/bin/bash
##############################################################
# File Name:check_url_while.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
# 调用函数库
func_include=/server/scripts/shell-pro/func_color.sh
if [ -f $func_include ];then
. $func_include
else
echo "函数库 $func_include 不存在,请检查路径"
exit 1
fi
# 参数
url=$1
count=0
pid=$$ # 脚本进程id
# 判断参数是否为空
check_param(){
[ -z "${url}" ] && {
echo "请输入域名$0 url"
exit 1
}
}
# 记录pid
write_pid(){
echo "$0 $pid" >/run/scripts.pid
[ $? -ne 0 ] && {
echo "pid 写入失败"
exit 1
}
}
# 检查命令
check_cmd() {
which curl >/dev/null 2>&1
if [ $? -ne 0 ];then
redecho 命令curl不存在
exit 2
fi
}
# 检查网站
check_url(){
while true
do
curl -L $url >/dev/null 2>&1
if [ $? -ne 0 ];then
let $count++
[ $count -ge 5 ] && {
echo "网站访问失败"
$count=0
}
else
echo "网站访问成功"
fi
sleep 5
done
}
main(){
check_param
write_pid
check_cmd
check_url
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash check_url_while.sh qq.com
网站访问成功
网站访问成功
10.3.4 while循环-读取文件内容
- 应用场景:
- 需要在脚本中读取文件内容,多行。
- 此时可以选择3剑客或while循环.
使用while + read的形式去读取文件或管道内容.
查看代码
read line #变量名字
#方式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
# 方式3:在while循环结尾done通过输
#重定向指定读取的文件。 推荐使用.
while read line
do
cmd
done<FILE
#使用建议
如果前面是命令使用方式2即可管道形式.
如果是文件一般使用方式3即可.
通过while read方式,统计ip.txt文件,次数大于5,ping下.
while_read_total_ip.sh
# 准备数据
cat >/server/files/ip.txt<<EOF
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
EOF
#!/bin/bash
##############################################################
# File Name:while_read_total_ip.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
src_path="/server/files/ip.txt"
func_include="/server/scripts/shell-pro/func_color.sh"
# 判断文件是否存在
file_isexist(){
if [ -f $func_include ];then
. $func_include
else
echo "函数库 $func_include 不存在,请检查路径"
exit 1
fi
}
# 读取文件
read_line(){
while read line
do
#ip=`echo $line+awk取列`
ip=`echo $line| awk '{print $1}'`
count=`echo $line| awk '{print $2}'`
if [ $count -gt 5 ];then
ping -c1 $ip >/dev/null 2>&1
[ $? -eq 0 ] && greenecho "$ip 可以访问" || redecho "$ip 不可以访问"
fi
done <$src_path
}
main(){
file_isexist
read_line
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_read_total_ip.sh
10.0.0.36 可以访问
10.0.0.37 不可以访问
baidu.com 可以访问
# 如果文件列数较少,while读文件也可以用如下方法
while read ip count
do
#ip=`echo $line+awk取列`
if [ $count -ge 5 ];then
ping -c 1 $ip &>/dev/null
if [ $? -eq 0 ];then
greenecho "$ip 可以访问"
else
redecho "$ip 不可以访问"
fi
fi
done <$src_file
while读取文件的方法2 vs 方法3 区别
while_read_dif.sh
#!/bin/bash
##############################################################
# File Name:while_read_dif.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
file=/server/files/ip.txt
i=0
j=0
#01
echo "方法2:while + cat"
cat $file |while read ip
do
echo $ip
let i++
done
echo "次数 $i"
#02
echo "方法3:while + 输入"
while read ipaddr
do
echo $ipaddr
let j++
done<$file
echo "次数 $j"
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash while_read_dif.sh
方法2:while + cat
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
次数 0
方法3:while + 输入
10.0.0.2 5
10.0.0.36 6
10.0.0.37 8
baidu.com 10
jd.com 5
次数 5
# 方法2(while read+管道)在运行的时候因为管道,创建1个子shell,变量都存放在子shell中,子shell运行完成,消失了,变量也没了。
# 方法3运行的时候是与当前脚本在同一个shell中,所以变量都保持了,可以继续使用。
案例42(学了iptables后可以解决):分析ngx访问日志找出访问量最高前5个 ip及他们的访问次数,IP访问次数大于200,通过防火墙屏蔽ip
防DOS,拒绝式服务攻击.
DDOS分布式拒绝式服务攻击.
CC 基于http请求攻击.
10.3.5 小结
- while循环
- 一个场景就是while :或true死循环.
- 一个场景就是while read 读取文件内容.
10.4 do-until循环
#直到型循环: 条件不满足后结束循环
until 条件
do
命令
命令
....
done
i=1
until [ $i -gt 10 ]
do
echo $i
let i++
done
10.5 循环小结
循环 | 格式 | 应用场景 |
for 循环 | for 变量 in 清单(列表 ) ;do 命令 ;done | 大部分使用 |
for((i=1;i<=10;i++)) ;do echo $i ;done | 数组循环 | |
while | while 条件 ;do 命令 ;done | 死循环,读文件 |
until循环 | until 条件 ;do 命令;done | 很少用. |
10.6 循环控制语句
流程控制语句
10.6.1 概述
语句 |
含义 | 应用场景 |
exit
|
终止执行脚本,退出返回值. exit n 范围:0-255 |
基础用法,判断后加上exit 脚本结束加上exit 用于脚本中检查部分(参数个数,检查格 式,adv: rc=0 ,可以放在脚本最后 exit $rc) |
return
|
放在函数中,终止`,函数返回值 |
写在函数中,检查函数命令运行是否成功。方便调试. 返回值:最后1个命令或函数中关键命令. |
循环控制语句 |
||
break |
终止循环(退出循环),不会继续运行剩余循环 |
要在循环中表示退出循环。 |
continue |
跳过本次循环,进入下一次循环 |
要在循环中跳过某一次循环。 |
return
function check_wget() {
#$url 是for循环中创建的
wget --spider $url >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ];then
greenecho $url可以访问
else
redecho $url 无法访问
fi
return $rc
}
10.6.2 实战
# continue
for n in {1..10}
do
[ $n -eq 5 ] && continue
echo $n
done
# break
for n in {1..10}
do
[ $n -eq 5 ] && break
echo $n
done
10.6.3 猜测数字
- 生成随机数字,判断数字是什么(1-100)
- 如果输入的数字比随机数大,提示大了,
- 如果输入数字比随机数小,提示小了,
- 如果等于提示恭喜并退出脚本
- 如果输入错误则提示错误并让用户重新输入(continue)
- 如果猜的次数超过10次则结束并输出数字(这里用下break退出)
guess_num.sh
#!/bin/bash
##############################################################
# File Name:guess_num.sh
# Version:V1.0
# Author:xk
# Organization: none
# Desc:
##############################################################
guess_num=$((RANDOM%100+1))
count=0
# 检查数字
check_num(){
read -p "请输入1-100内的数字:" num
[[ $num =~ ^[0-9]+$ ]] || {
echo "请输入数字"
exit 1
}
}
# 比较
compare(){
while true
do
check_num
let count++
if [ $num -ne $guess_num ];then
[ $count -gt 7 ] &&{
echo "次数用完了"
break
}
if [ $num -gt $guess_num ] ;then
echo "猜测的数比随机数大"
else
echo "猜测的数比随机数小"
fi
continue
else
echo "猜对了,数字为 $guess_num"
break
fi
done
total
}
# 总结
total(){
#conut1=`awk -vn=$count 'BEGIN {print n - 1}'`
count1=`echo $count -1 | bc`
echo "一共猜测了 $count 次, 猜错了 $count1 次"
}
main(){
compare
}
main
[root@Kylin-V10-sp3 /server/scripts/shell-pro]# bash guess_num.sh
请输入1-100内的数字:50
猜测的数比随机数小
请输入1-100内的数字:75
猜测的数比随机数大
请输入1-100内的数字:62
猜测的数比随机数大
请输入1-100内的数字:56
猜测的数比随机数大
请输入1-100内的数字:53
猜测的数比随机数大
请输入1-100内的数字:52
猜测的数比随机数大
请输入1-100内的数字:51
猜对了,数字为 51
一共猜测了 7 次, 猜错了 6 次
10.6.4 案例
10.6.4.1 return
start_stop_nginx
start_nginx() {
# Start the daemon/service
#
# Returns:
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS 2>/dev/null || return 2
}
stop_nginx() {
# Stops the daemon/service
#
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
RETVAL="$?"
sleep 1
return "$RETVAL"
}
10.6.4.2 break&continue
break n 结束多少层循环
continue n 结束当前循环,并从第几层运行
for i in {1..5}
for i in {1..5}
do
for j in {a..c}
do
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && continue
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && continue 2
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && break
echo $i $j
done
done
for i in {1..5}
do
for j in {a..c}
do
[ $j = b ] && break 2
echo $i $j
done
done
10.6.5 小结
要求 熟练掌握break和continue 含义即可。
熟练使用exit
逐渐在函数中使用return.
11.数组
11.1 概述
- 数组也是一种变量. 一组数据
- 数组可以存放多个 相关联内容 ,通过访问数组调用结果(值)
- 应用场景:
- 用于存放相关的数据。
- 获取用户连续的输入.
- 如何创建数组(手动)
# 创建数组
[root@Kylin-V10-sp3 ~]# ip_array=(10.0.0.61 10.0.0.71 10.0.0.81)
# 输出数组中的内容(ip_array[0] 数组中的某个元素 0表示下标,元素名称.)
[root@Kylin-V10-sp3 ~]# echo ${ip_array[0]}
10.0.0.61
[root@Kylin-V10-sp3 ~]# echo ${ip_array[2]}
10.0.0.81
# 输出数组中所有值。
[root@Kylin-V10-sp3 ~]# echo ${ip_array[*]}
10.0.0.61 10.0.0.71 10.0.0.81
[root@Kylin-V10-sp3 ~]# echo ${ip_array[@]}
10.0.0.61 10.0.0.71 10.0.0.81
# 计算数组的元素的个数
[root@Kylin-V10-sp3 ~]# echo ${#ip_array[*]}
3
[root@Kylin-V10-sp3 ~]# echo ${#ip_array[@]}
3
[root@Kylin-V10-sp3 ~]
shell数组,下标或元素名字,默认只能是数字。
要在shell数字中,下标中使用字符串,需要创建关联数组。(awk中使用更多,默认就是这个)
shell数组的循环
# 推荐的形式 ⭐
[root@Kylin-V10-sp3 ~]# names=(jd.com baidu.com taobao.com 12306.cn)
[root@Kylin-V10-sp3 ~]# for n in ${names[@]}
> do
> ping -c 1 -W 1 $n
> done
PING jd.com (211.144.24.218) 56(84) bytes of data.
64 bytes from 211.144.24.218 (211.144.24.218): icmp_seq=1 ttl=128 time=7.30 ms
--- jd.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.302/7.302/7.302/0.000 ms
PING baidu.com (110.242.68.66) 56(84) bytes of data.
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=128 time=11.7 ms
--- baidu.com ping statistics ---
...
[root@Kylin-V10-sp3 ~]#
#其他形式
names=(jd.com baidu.com taobao.com 12306.cn)
for((i=0;i<${#names[@]};i++))
do
ping -c 1 -W 1 ${names[i]}
done
11.2 shell数组赋值
shell数组 创建(赋值) |
具体形式 | 应用场景 |
批量直接赋值
|
array=(ip01 ip02 ip03 ip04 ) ⭐ array=( 命令结果 ) # cat ip.txt 三剑客命令获取指定内容 |
应用最多. |
逐个元素赋值
|
array[0]=a array[1]=b array[2]=c ...... |
几乎不会使用. |
read命令赋值 |
read -p "输入数组中内容:" -a array 可以创建数组,空格分割即可. |
用于连续读取数据,用户输入3个数 |
赋值
[root@Kylin-V10-sp3 /server/files]# cat >/server/files/ip2.txt<<EOF
> 10.0.0.5
> 10.0.0.6
> 10.0.0.7
> 10.0.0.8
> 10.0.0.9
> EOF
[root@Kylin-V10-sp3 /server/files]# ip_list=(`cat ip2.txt`)
[root@Kylin-V10-sp3 /server/files]# echo ${ip_list[0]}
10.0.0.5
[root@Kylin-V10-sp3 /server/files]# echo ${ip_list[*]}
10.0.0.5 10.0.0.6 10.0.0.7 10.0.0.8 10.0.0.9
[root@Kylin-V10-sp3 /server/files]# echo ${#ip_list[@]}
5
[root@Kylin-V10-sp3 /server/files]#
11.3 小结
- 如何赋值(如何创建)(通过命令结果)。
- 如何取值,统计数组元素的个数。
- 数组与for循环。
- 未来常用的是awk数组。
12. 堡垒机
12.1 shell脚本实现
功能:
1. 登录堡垒机的机器,自动运行堡垒机脚本
2. 显示菜单选择功能
2.1 远程连接
1 web01
2 web02
3 lb01
4 db01
5 nfs01
2.2 测试ping,telnet
ping
telnet
# 不能给登录堡垒机的用户root权限,给了root权限也要让无法执行命令.
12.2 堡垒机核心功能
环境准备:
先配置好堡垒机到其他机器的密钥认证.
书写脚本:
备注:
整体功能OK:
存在问题:
🅰 脚本需要手动运行.
🅱 脚本一次性.
12.3 自动运行脚本
脚本放在/etc/profile.d/xxxx.sh结尾即可.
用户连接运行这里面的脚本.
软连接过去,调试完成可以复制
ln -sf /server/scripts/devops-shell/39.bao.sh /etc/profile.d/
设置死循环
12.4 脚本禁止取消或结束
屏蔽ctrl+c
- trap 获取指定的信号. 用于获取指定操作产生的信号.
- ctrl + c对应的信号SIGINT
- 通过trap获取SIGINT信号(ctrl+c),获取后不执行任何任务.
#通过trap获取SIGINT信号(ctrl+c),获取后不执行任何任务.
trap '' SIGINT
脚本开始.
12.5 完整版本代码
baoleiji.sh
[root@m01-devops-shell /server/scripts/devops-shell]# cat
39.bao.sh
#!/bin/bash
#############################################################
#
# File Name:39.bao.sh
# Version:V1.0
# Author:xk
# Organization:
# Desc:
#############################################################
#
#0.trap
trap '' SIGINT
#1. main_menu主菜单
main_menu() {
clear
cat <<EOF
#############堡垒机-主菜单############
a: 远程连接
#############################################
EOF
}
ssh_menu() {
clear
cat <<EOF
#############堡垒机-远程连接菜单############
1. 连接 10.0.0.5
2. 连接 10.0.0.7
3. 连接 10.0.0.51
#############################################
EOF
}
test_menu() {
clear
cat <<EOF
#############堡垒机-调试工具菜单############
1. ping
2. telnet
#############################################
EOF
}
#2.input获取用户输入
input() {
read choice
}
#test部分
#ping
check_ping() {
read -p "请输入ping的ip:" ip
#还可以加入判断
ping -c2 -i0.5 $ip
#还可以加入判断返回值
}
#telnet
check_telnet() {
read -p "请输入ping的ip和端口" ip port
#还可以加入判断
telnet $ip $port
#还可以加入判断返回值
}
#ssh_all
ssh_all() {
#远程连接
ssh_menu
input
case "$choice" in
1) ssh root@10.0.0.5 ;;
2) ssh root@10.0.0.7 ;;
3) ssh root@10.0.0.51 ;;
*)
echo "选择错误,请重新输入"
continue
esac
}
#test_all
test_all() {
#执行测试命令
test_menu
input
case "$choice" in
1) check_ping ;;
2) check_telnet ;;
*)
echo "选择错误,退出"
continue
esac
}
#3.main
main() {
while true
do
main_menu
input
case "$choice" in
a|A) ssh_all ;;
b|B) test_all ;;
oldboy) break ;;
*)
echo error
continue
esac
done
}
main
13. Debug全流程
13.1 书写习惯
# 书写习惯
1. 注释步骤,变量,函数.
2 变量:在脚本中尽量使用变量,变量命名规范(给变量加上说明)。
3. 函数:代码中尽可能使用函数,增加说明。 模块化.
4. 返回值: 尽可能增加函数return功能和日志功能,方便后期调试。
5. 参数与选项检查: 进可能增加exit 返回值的功能,方便后期调试。
6. 书写的时候适当增加输出:书写代码的时候,可以多写一些echo用于在某些步骤中进行输出。
7. 缩进:代码注意缩进。
8. 成对的符号提前输入好. "" [] {}
13.2 调试方法
# 调试方法
1. x大部分时候使用:sh/bash -x显示详细的执行过程。
2. 精确显示过程脚本使用functions函数库,脚本过大,可以在脚本中使用
set -x 开始显示详细信息
代码(一般为main中调用的函数名)
set +x 关闭显示详细信息
3. 注释法使用函数,注释法。注释多余的函数,排除。缩小范围定位问题
4.输出关键变量变量后增加echo输出变量内容。查看过程。
# 书写脚本指定为英文
export LANG=en_US.UTF-8
shell的严格模式,变量不存在则退出,命令执行失败则退出
shell的严格模式
set -eu #
-e 只要执行失败,就结束脚本.就相当于exit
-u 调用了未定义的变量,则报错
# 大部分时候使用:sh -x显示详细的执行过程。
# 排除法
# 命令行也可以用调试
[root@Kylin-V10-sp3 /server/files]# set -x
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]#
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]# ls
+ ls --color=auto
array-add.txt awk-array.txt ip2.txt ip.txt urls url.txt
++ printf '\033]0;%s@%s:%s\007' root Kylin-V10-sp3 /server/files
[root@Kylin-V10-sp3 /server/files]# set +x
+ set +x
[root@Kylin-V10-sp3 /server/files]#
14. shell(三剑客)
14.1 sed与变量
14.1.1 sed命令中如何调用变量
cat > ip.txt<<EOF
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
EOF
src=10.0.0
dst=172.16.1
sed "s#$src#$dst#g" ip.txt
172.16.1.5
172.16.1.6
172.16.1.7
172.16.1.8
172.16.1.9
14.2 awk
14.2.1 指哪打哪
# 过滤出/etc/passwd的第2到9行的第1列和第3列
[root@Kylin-V10-sp3 ~]# awk -F: 'NR>=2 && NR<=9{print $1,$3}' /etc/passwd |column -t
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
[root@Kylin-V10-sp3 ~]#
# 第1列用户名等于root的最后一列(命令解释器)。
[root@Kylin-V10-sp3 ~]# awk -F: '$1=="root"{print $NF}' /etc/passwd
/bin/bash
# 进阶:第1列用户名等于root的最后一列(命令解释器) 使用变量方法
'''
命令行/shell变量
name=root
-v n变量=内容
取出/etc/passwd中第1列 等于root行.
'''
[root@Kylin-V10-sp3 ~]# awk -vn=$name '$1==n{print $NF}' /etc/passwd
[root@Kylin-V10-sp3 ~]# awk -F: -vn=$name '$1==n{print $NF}' /etc/passwd
[root@Kylin-V10-sp3 ~]# awk -F: -vn=$name '$1~n{print $NF}' /etc/passwd | head -3
/bin/bash
/sbin/nologin
/sbin/nologin
# 过滤出网卡文件中ip地址那行
[root@Kylin-V10-sp3 ~]# awk -F= '/IPADDR/{print $2}' /etc/sysconfig/network-scripts/ifcfg-eth0
10.0.0.36
14.2.2 awk的判断与循环
14.2.2.1 判断
如果系统根分区磁盘使用率大于80,提示磁盘空间不足
if (条件) {
满足条件执行什么内容
}
if (条件) {
满足条件执行什么内容
}
else {
}
df -h|awk '$NF=="/"{ if($5>=8) print "磁盘空间不足" }'
磁盘空间不足
df -h|awk '$NF=="/"{ if($5>=8) print "磁盘空间不足" }'
awk '$NF=="/"{
if($5>=8)
print "磁盘空间不足"
}'
awk 'NR<=5' /etc/passwd
awk '{if(NR<=5){print $0}}' /etc/passwd
awk '{
if(NR<=5){
print $0
}
}' /etc/passwd
[root@m01 ~]# df -h |awk -F"[ %]+" '$NF=="/"{if( $(NF-1)>=5 )
print "磁盘空间不足" }'
磁盘空间不足
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]#
[root@m01 ~]# df -h |awk -F"[ %]+" '$NF=="/" && $(NF-1) >=80
{ print "磁盘空间不足" }'
温馨提示:
# awk进行统计计算的时候,如果是数字+字符,变成字符串对比。左到右一个字符一个字符对比。
解决方案01⭐ :避免这个情况,通过awk指定分隔符,只获取数字部分即可。
解决方案02:通过让这个字符串进行运算,运算后会被转换为数字。
df -h|awk '$NF=="/"{ if($5+0 >= 8) print $0 }'
14.2.2.2 循环
for(i=1;i<=100;i++) {
sum=sum+i
}
print sum
awk 'BEGIN{for(i=1;i<=100;i++) {sum=sum+i} print sum }'
awk 'BEGIN{
for(i=1;i<=100;i++)
{sum=sum+i}
print sum
}'
# 计算任意数字的总和.
[root@m01 ~]# read -p "请输入数字空格分割:" num
请输入数字空格分割:1 2 4 5 66
[root@m01 ~]# echo $num
1 2 4 5 66
[root@m01 ~]# echo $num |awk '{for(i=1;i<=5;i++) sum=sum+$i ;print sum }'
78
[root@m01 ~]# echo $num |awk '{for(i=1;i<=NF;i++) sum=sum+$i ;print sum }'
78
14.2.3 数组
14.2.3.1 概述
- awk数组专用于统计与分析。
- 去重统计次数(sort+uniq)。
- 去重求和。
- awk数组与shell数组区别
- awk数组: 关联数组,下标啥都行。
- shell数组:普通数组,下标数字,shell中也有关联数组.。
14.2.3.2 使用
# 创建
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos" }'
# 取值
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos"; print array[0]}'
a
[root@Kylin-V10-sp3 ~]# awk 'BEGIN{array[0]="a";array["name"]="abc";array[110]="sos"; print array["name"]}'
abc
14.2.3.2.1 批量赋值
# 创建以url为下标,元素值是次数数组。输出数组内容。
cat >/server/files/awk-array.txt<<EOF
baidu.com 6
qq.com 7
aliyun.com 99
EOF
[root@Kylin-V10-sp3 ~]# awk '{ip_array[$1]=$2} END {for (url in ip_array) print url ip_array[url]}' /server/files/awk-array.txt
aliyun.com99
baidu.com6
qq.com7
[root@Kylin-V10-sp3 ~]#
14.2.3.2.1 去重统计次数 (sort+uniq)
[root@Kylin-V10-sp3 ~]# cat >/server/files/url.txt<<EOF
> http://www.baidu.org/index.html
> http://www.baidu.org/1.html
> http://post.baidu.org/index.html
> http://mp3.baidu.org/index.html
> http://www.baidu.org/3.html
> http://post.baidu.org/2.html
> EOF
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# awk -F'/+' '{url[$2]=url[$2]+1}END{ for(name in url )print name,url[name]}' /server/files/url.txt
mp3.baidu.org 1
post.baidu.org 2
www.baidu.org 3
[root@Kylin-V10-sp3 ~]# awk -F'/+' '{url[$2]++}END{ for(name in url )print name,url[name]}' /server/files/url.txt
mp3.baidu.org 1
post.baidu.org 2
www.baidu.org 3
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# cat >/server/files/array-add.txt<<EOF
> a 1
> b 2
> c 3
> a 4
> a 5
> b 6
> d 7
> EOF
[root@Kylin-V10-sp3 ~]#
[root@Kylin-V10-sp3 ~]# awk '{array[$1]++} END {for(i in array) print i,array[i]}' /server/files/array-add.txt
a 3
b 2
c 1
d 1
# 第二列数字加起来
[root@Kylin-V10-sp3 ~]# awk '{array[$1]=array[$1]+$2} END {for(i in array) print i,array[i]}' /server/files/array-add.txt
a 10
b 8
c 3
d 7
14.2.3.2.1 统计access.log中每个ip地址的流量总数(awk数组)
# 第1列是ip地址 第10列是流量(单位是字节)
[root@Kylin-V10-sp3 ~/test]# awk '{flow_array[$1]=flow_array[$1]+$10} END {for (i in flow_array) print i, flow_array[i]}' access.log | sort -rnk2 | head
114.83.184.139 31362956
117.136.66.10 22431302
116.216.30.47 21466000
223.104.5.197 21464856
116.216.0.60 19145329
114.141.164.180 17219553
114.111.166.22 17121524
223.104.5.202 16911512
116.228.21.187 15969887
112.64.171.98 15255013
[root@Kylin-V10-sp3 ~/test]#
#本质:ip地址是一样的然后把第10列相加。
flow_array[$1]=flow_array[$1]+$10
flow_array[$1]+=$10
sum=sum+$10 === sum+=$10
14.2.4 awk与shell语法格式对比
具体语法 |
awk | shell编程 |
if单分支判断 |
if (条件) {命令,多个命令} |
if 条件;then 命令 fi |
if双分支判断 |
if (条件) {命令} else {命令} |
if 条件;then 命令 else 命令 fi |
for循环:c语言形式 |
for(i=1;i<=10;i++) {命令;命令2} |
for((i=1;i<=10;i++)) do 命令 done |
for循环:通用 |
awk专用于数组的循环 for( n in 数组名字) print n(变量n数组下标),数组名字[n] |
for n in 清单 do 命令 done |
15. 总结
- shell编程环境
- shell执行方式sh/bash
- 变量取值,赋值,命名.
- 普通变量${} ,环境变量(了解)
- 案例-登录系统后输出基本信息。
- 特殊变量 $n,$0,$#,$?
- 其他特殊变量:能够认识,知晓含义即可.
- 案例:多种命令,多种传参方式实现计算器