Lupinus

Re:从零开始的go学习生活(`・ω・´)

0%

[Shell编程]

shell基础入门

为什么使用shell

1.安装操作系统(CentOS)自动化安装操作系统(kickstart cobbler)底层shell

2.初始化/优化操作系统

  • ntp时间同步
  • 更改默认yum源
  • ssh优化
  • 关闭Selinux
  • 关闭/开启 防火墙(C6:iptables C7:firewalld)
  • 安装基础服务(wget vim lrzsz net-tools unzip gzip…)
  • 优化文件描述符
  • 优化字符集

3.安装服务

  • Nginx
  • PHP
  • MySQL
  • Redis
  • MHA
  • Rsync
  • NFS
  • MongoDB
  • Zabbix

4.启动服务(系统默认的shell脚本)

5.脚本实现自动化代码上线

6.监控服务(使用shell)

7.结合定时任务使用shell

8.重复性工作写入脚本

  • 日志切割
  • 日志分析
  • 数据统计
  • 机器巡检
  • 数据备份

shell编程需要掌握的基础知识

  • 熟练使用vim编辑器
  • 熟悉ssh终端(Xshell、CRT)
  • 熟练掌握linux常用命令
  • 熟练掌握linux正则表达式及三剑客命令
  • VScode

shell脚本规范

1.目录统一
2.shell脚本的结尾要以.sh结尾
3.脚本的开头需要有解释器

1
#!/bin/bash

4.作者信息

1
2
3
4
#!/bin/bash
#Author: _Roger_
#Date: _1999-12-21_
#Name: _Print Message_

5.一定要有注释(可以中文)

6.shell中尽量使用英文

7.成对的符号和语句一次性写完

vim模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1.首先先编辑一个模板文件,该模板文件可以叫任何名字
[root@zabbix01 ~]# vim /usr/share/vim/vimfiles/template.roger
#!/bin/bash
#Author: _Roger_
#Date: _1999-12-21_
#Name: _Print Message_

# 2.写完之后,我们需要修改一下vim的配置文件
[root@zabbix01 ~]# vim /etc/vimrc
autocmd BufNewFile *.spec 0r /usr/share/vim/vimfiles/template.spec
## 在第28行,autocmd自动保存模板文件,修改一下,因为我们是要写shell脚本的模板
## 所以我们要把*.spec 修改成*.sh
## 然后将后面的模板文件改成你定义的模板文件名
autocmd BufNewFile *.sh 0r /usr/share/vim/vimfiles/template.roger

# 3.编辑~/.vimrc
[root@zabbix01 ~]# vim ~/.vimrc
autocmd bufNewFile *.py,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1, "#!/bin/bash")
call setline(2, "")
call setline(3, "# File Name: __".expand("%") . "__")
call setline(4, "# Version: __v1.1__ ")
call setline(5, "# Author: __RogerWang__ ")
call setline(6, "# Mail: __690705712@qq.com__ ")
call setline(7, "# Blog: __https://rogerxs80.github.io/__ ")
call setline(8, "# DateTime: __".expand(strftime("%Y-%m-%d %H:%M")) . "__")
endif
endfunc

# 4.接下来,我们编辑所有只要以sh结尾的文件,都会带有作者信息
[root@zabbix01 ~]# vim test_roger.sh

脚本执行方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@m01 ~]# vim test.sh
#!/bin/bash
#Author: _Roger_
#Date: _1999-12-21_
#Name: _Print Message_

echo 'Hello World'

#执行脚本
[root@m01 ~]# sh test.sh
Hello World
[root@m01 ~]# bash test.sh
Hello World
[root@m01 ~]# ./test.sh
Hello World
[root@m01 ~]# /root/test.sh
Hello World
[root@m01 ~]# . test.sh
Hello World
[root@m01 ~]# source test.sh
Hello World

. 和 source 都是在父shell下执行的
sh , bash ,相对路径 ,绝对路径都是在子shell下执行的

开发语言中程序代码分类

  • 编译型
  • 解释型

shell中的变量

变量介绍

变量即变化的量,核心是“变”与“量”二字,变即变化,量即衡量状态

量:是记录现实世界当中的某种状态
变:指的是记录的状态是可以发生变化的

1
2
name=roger
age=18

如何使用变量

1
2
3
变量名 赋值符号 变量值
name=roger #shell中定义变量
name: roger #yaml中定义变量

定义变量的语法(分三部分):
1)变量名
相当于一个门牌号,便于取出变量值,是访问到值的唯一方式

2)赋值符号
将值的内存地址,绑定给变量名

3)变量值
用来表示状态

变量的使用规则:先定义,在通过变量名去引用

定义变量名的规范

变量名的命名规则:
1.大前提:变量名的命名应该能够反映出值记录的状态
2.变量是用来访问变量值的,所以变量名应该遵循一定规范,来方便我们标识存到内存中值的功能

1
2
3
4
5
1.变量名只能是 字母、数字或下划线的任意组合(区分大小写)
2.变量名不可以使用中文
3.变量名,不要使用命令来命名
4.不要用拼音
5.变量名不能以数字开头

变量名定义的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.下划线+纯小写
name_of_ww='roger'
name_of_xxx='roger'

2.驼峰体
nameOfWw='roger'
nameOfWwww='roger'
ageOfWwww=73

3.下划线+纯大写
NAME_OF_WWW='xxx'

不好的方式:
1)变量名为中文、拼音
2)变量名过长
3)变量名词不达意

变量的分类

系统内置环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
## 查看系统环境变量
env
declare
export

## 环境变量
LANG #字符集语言
PATH #该变量中的路径里的所有可执行文件,都可以直接执行,不需要加路径
PS1 #命令提示符
UID #当前登录用户的uid
HOSTNAME #当前主机名
PWD #当前工作路径
USER #当前登录的用户名

## 历史记录相关
HISTSIZE
HISTFILESIZE
HISTFILE
TMOUT
HISTCONTROL:export HISTCONTROL=ignorespace #离职专用变量
PROMPT_COMMAND:export PROMPT_COMMAND #跳板机专用变量

普通变量

1
2
3
4
5
6
DATE=$(date +%F-%T)
IP=$(ifconfig eth0|awk 'NR==2{print $2}')

## 注意使用大括号分隔变量
mkdir $DATE_$IP_$HOSTNAME #基本凉了
mkdir ${DATE}_${IP}_${HOSTNAME}

位置变量

1
2
3
4
5
6
7
8
9
$N:N正整数,$1 $2 $3...$N  两位数要使用{}, ${11} ${10}
$0:执行脚本的路径和名字
$#:传递参数的个数
$*:
1.不适合数组使用
2.调用是加双引号 #接收脚本后面所有的参数
$@
1.适合后面数组数据类型使用
2.调用是加双引号 #接收脚本后面所有的参数

特殊变量(状态)

1
2
3
4
5
6
7
$?:表示上一个命令执行的状态(上一条命令的返回值)0 成功执行 非0失败 
特殊命令:
- false
- diff
$$:表示当前脚本执行的pid
$!:上一个脚本或者程序运行的pid
$_:获取上一条命令的最后一个参数(以空格为分隔符) ESC + .

变量的子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
${#变量名}:获取该变量的值长度 
${变量名}:调用变量
${变量名:偏移量}:字符串的截取
${变量名:偏移量:步长}:字符串的截取
time for n in {1..10000};do echo ${#n} >/dev/null;done
time for n in {1..10000};do echo ${n}|wc -L >/dev/null;done
time for n in {1..10000};do echo ${n}|awk '{print length()}' > /dev/null;done

${变量名#字符串}:从变量开头,删除最短匹配word的子串
${变量名##字符串}:从变量开头,删除最长匹配word的子串
for line in `cat 1.txt`;do
echo ${line##*/}
echo ${line#*/}
done

${parameter%word} 从变量结尾,删除最短匹配的word的子串
${parameter%%word} 从变量结尾,删除最长匹配的word的子串
${parameter/pattern/string} 使用string替换第一个pattern
${parameter//pattern/string} 使用string替换所有的pattern

企业面试题 I am oldboy linux's teacher Roger,welcome to our trainning.请打印出这句话中,单词大于6的单词并显示个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 思路分析
1.如何将一句话变成一个一个的单词
2.将每一个单词拿出来跟6进行对比
3.如果大于6就打印
4.如果大于6就打印出具体个数

# 思路转代码
echo "I am oldboy linux's teacher Roger,welcome to our trainning." |tr ',.' ' '

for word in I am oldboy linux's teacher Roger welcome to our trainning;do
if [ ${#word} -gt 6 ];then
pass
fi
done

echo "单词:$word"

echo "长度:${#word}"

# 放入脚本
[root@m01 script]# vim 02_sub_string.sh
#!/bin/bash

text=`echo "I am oldboy linux's teacher Roger,welcome to our trainning." |tr ',.' ' '`

for word in $text;do
if [ ${#word} -gt 6 ];then
echo "单词:$word"
echo "长度:${#word}"
fi
done

[root@m01 script]# sh 02_sub_string.sh
单词:linux's
长度:7
单词:teacher
长度:7
单词:welcome
长度:7
单词:trainning
长度:9

## 美化
#!/bin/bash

text=`echo "I am oldboy linux's teacher Roger,welcome to our trainning." |tr ',.' ' '`

for word in $text;do
if [ ${#word} -gt 6 ];then
echo '------ 单词统计结果 ------'
echo "单词:$word"
echo "长度:${#word}"
fi
done

## 纯awk方法(简单了解,这里只是为了让你们看看awk的牛逼之处)
[root@m01 script]# echo "I am oldboy linux's teacher Roger,welcome to our trainning."|awk -F '[ ,.]' '{for(i=1;i<=NF;i++) if(length($i) >6) print "单词:"$i,"长度:" length($i)}'

# 美化
[root@m01 script]# echo "I am oldboy linux's teacher Roger,welcome to our trainning."|awk -F '[ ,.]' '{for(i=1;i<=NF;i++) if(length($i) >6) print "------ 单词统计结果------\n" "单词:"$i"\n长度:" length($i)}'
------ 单词统计结果------
单词:linux's
长度:7
------ 单词统计结果------
单词:teacher
长度:7
------ 单词统计结果------
单词:welcome
长度:7
------ 单词统计结果------
单词:trainning
长度:9

shell运算符

基础运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+:加
-:减
*:乘
/:除
%:取余

## 取余案例
echo $((RANDOM%10))

## 自增案例
for ((i=1;i<=10;i++));do
echo $i
done

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

逻辑符号

1
2
3
&&:且
||:或
!:非

所有符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+、-	加号,减号,一般用于整型、浮点型等类型的运算,其他语言中也可以做字符串的拼接
*、/、% 乘号、除号、取余,一般用于运算

-------
**:幂运算
++、-- 自增、自减,可前置也可后置,默认步长为1
n++:n=n+1
n--:n=n-1

n++ n+=1 n=n+1

n=n+2 n+=2
n=n-2 n-=2
n=n*2 n*=2
n=n/2 n/=2

n=n%2 n%=2

==:等于 #equal -eq
<:小于 #less than -lt
>:大于 #greate than -gt
>=:大于等于 #greate equal -ge
<=:小于等于 #less equal -le
<> 或 != :不等于 #ot equal -ne

=:赋值符号
==:运算符号,等于

>>:向右位移
<<:向左位移

~、|、&、^:按位取反、按位异或、按位与、按位或

运算方式

(())

1
2
3
4
5
6
echo $((1+2))
echo $((26/5))

a=1
b=2
echo $((a+b))

let

1
2
3
4
5
let a=1+2
echo $a

let b=26/5
echo $b

expr

1
2
3
4
expr 1 + 1
expr 1+1 #错误写法
expr 2 * 2.5 #错误写法
expr 26 / 5

bc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@m01,172.16.1.61:~ # echo 1+2|bc
root@m01,172.16.1.61:~ # echo 26/5|bc
root@m01,172.16.1.61:~ # echo 2*2.5|bc
root@m01,172.16.1.61:~ # echo 26/5|bc -l
5.20000000000000000000

## 圆周率
echo 'scale=100;a(1)*4'|bc -l
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170676

## 进制转换
root@m01,172.16.1.61:~ # echo 'obase=2;255'|bc
11111111
root@m01,172.16.1.61:~ # echo 'obase=2;192'|bc
11000000
root@m01,172.16.1.61:~ # echo 'obase=16;192'|bc
C0
root@m01,172.16.1.61:~ # echo 'obase=16;255'|bc
FF

awk

1
2
3
4
root@m01,172.16.1.61:~ # awk 'BEGIN{print 26/5 1+2 3*8}'
5.2324
root@m01,172.16.1.61:~ # awk 'BEGIN{print 26/5,1+2,3*8}'
5.2 3 24

作业脚本:计算器

1
2
3
4
5
6
7
8
9
10
11
1.首先要传递2个参数(数字)
2.传少了报错
报错信息:至少传递两个整数...
3.传的不是数字报错
报错信息:请输入两个整数,不能是字母或特殊符号...
4.计算出传递两个数字的
- +
- -
- *
- /
- %

shell条件表达式

条件表达式介绍

条件表达式,我们非常的常用,可以说,任何编程语言,都离不开条件表达式,但是每种变成语言的写法都不太一样,在shell中,有一种独特的写法。

1
2
3
[ 条件 ]      ## 不支持 > <  支持:-eq -le -ne
[[ 条件 ]] ## 支持 > < -eq -le -ne
test 条件 ## 都支持 命令行使用test

条件表达式的选项

判断普通文件 -f file

1
2
3
4
5
6
7
8
9
-f:判断文件是否存在
root@m01,172.16.1.61:~ # test -f /tmp/1.txt
root@m01,172.16.1.61:~ # echo $?
0
root@m01,172.16.1.61:~ # test -f /tmp/2.txt
root@m01,172.16.1.61:~ # echo $?
test -f /tmp/2.txt && echo '存在' || echo '不存在'
[ -f /tmp/2.txt ] && echo '存在' || echo '不存在'
[[ -f /tmp/2.txt ]] && echo '存在' || echo '不存在'

判断目录-d directory

1
2
3
4
5
6
7
8
root@m01,172.16.1.61:~ # test -d /tmp/
root@m01,172.16.1.61:~ # echo $?
0
root@m01,172.16.1.61:~ # test -d /tmp
root@m01,172.16.1.61:~ # echo $?
test -d /tmp/ && echo '存在' || echo '不存在'
[ -d /tmp ] && echo '存在' || echo '不存在'
[[ -d /tmp ]] && echo '存在' || echo '不存在'

判断文件-e exists

1
2
3
4
5
6
既可以判断文件也可以判断目录
root@m01,172.16.1.61:~ # test -e /tmp/1.txt && echo '存在' || echo '不存在'
存在

root@m01,172.16.1.61:~ # test -e /tmp && echo '存在' || echo '不存在'
存在

判断文件有没有读取权限 -r read

1
test -r /tmp/1.txt && echo '可读' || echo '不可读'

判断文件有没有写入权限 -w write

1
test -w /tmp/1.txt && echo '可写' || echo '不可写'

判断文件有没有执行权限 -x execute

1
test -x /tmp/1.txt && echo '可执行' || echo '不可执行'

判断文件有没有内容 -s size

1
2
test -s /tmp/1.txt && echo '有内容' || echo '文件为空'
文件为空

判断文件是否是一个软链接 -L link

1
2
3
4
root@m01,172.16.1.61:~ # test -L /bin && echo '是软链接' || echo '不是软链接'
是软链接
root@m01,172.16.1.61:~ # test -L /tmp && echo '是软链接' || echo '不是软链接'
不是软链接

对比两个文件的新旧 -nt newer than

1
2
3
4
5
root@m01,172.16.1.61:~ # test /tmp/1.txt -nt /tmp/2.txt && echo '1.txt比2.txt新' || echo '1.txt不比2.txt新'
1.txt不比2.txt新
root@m01,172.16.1.61:~ # echo 1 > /tmp/1.txt
root@m01,172.16.1.61:~ # test /tmp/1.txt -nt /tmp/2.txt && echo '1.txt比2.txt新' || echo '1.txt不比2.txt新'
1.txt比2.txt新

对比两个文件的新旧 -ot oldder than

1
2
3
4
5
root@m01,172.16.1.61:~ # test /tmp/1.txt -ot /tmp/2.txt && echo '1.txt比2.txt老' || echo '1.txt不比2.tt老'
1.txt不比2.txt老
root@m01,172.16.1.61:~ # echo 2 > /tmp/2.txt
root@m01,172.16.1.61:~ # test /tmp/1.txt -ot /tmp/2.txt && echo '1.txt比2.txt老' || echo '1.txt不比2.txt老'
1.txt比2.txt老

字符串表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-n:判断字符串是否为空,非空则成立
root@m01,172.16.1.61:~ # abc=''
root@m01,172.16.1.61:~ # test -n "$abc" && echo '字符串不为空' || echo '字符串为空'
字符串为空
root@m01,172.16.1.61:~ # abc='sbwyk'
root@m01,172.16.1.61:~ # test -n "$abc" && echo '字符串不为空' || echo '字符串为空'
字符串不为空

-z:判断字符串是否为空,空则成立
root@m01,172.16.1.61:~ # abc='sbwyk'
root@m01,172.16.1.61:~ # test -z "$abc" && echo '字符串为空' || echo '字符串不为空'
字符串不为空
root@m01,172.16.1.61:~ # abc=''
root@m01,172.16.1.61:~ # test -z "$abc" && echo '字符串为空' || echo '字符串不为空'
字符串为空

'str1' = 'str2':字符串相等则成立
root@m01,172.16.1.61:~ # test 'abc' = 'abc' && echo '字符串相等' || echo '字符串不相等'
字符串相等
root@m01,172.16.1.61:~ # test 'abc' = 'abc1' && echo '字符串相等' || echo '字符串不相等'
字符串不相等

'str1' != 'str2':字符串不相等则成立
root@m01,172.16.1.61:~ # test 'abc' != 'abc' && echo '字符串不相等' || echo '字符串相等'
字符串相等
root@m01,172.16.1.61:~ # test 'abc' != 'abc1' && echo '字符串不相等' || echo '字符串相等'
字符串不相等

整数表达式

1
2
3
4
5
6
7
8
9
-eq:等于
-ne:不等于
-lt:小于
-le:小于等于
-gt:大于
-ge:大于等于

root@m01,172.16.1.61:~ # [[ 18\>=16 ]] && echo 1 || echo 2
1

[[]]正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
=~:成员运算
[[ =~ ]]

[root@m01 ~]# name=huanglong
[root@m01 ~]# [[ $name =~ 'h' ]] && echo '包含' || echo '不包含'
包含
[root@m01 ~]# [[ $name =~ 'j' ]] && echo '包含' || echo '不包含'
不包含
[root@m01 ~]# [[ $name =~ [0-9] ]] && echo '包含' || echo '不包含'
不包含
[root@m01 ~]# [[ $name =~ [a-z] ]] && echo '包含' || echo '不包含'
包含
[root@m01 ~]# [[ $name =~ ^[a-z] ]] && echo '包含' || echo '不包含'
包含
[root@m01 ~]# [[ $name =~ ^[a-z]$ ]] && echo '包含' || echo '不包含'
不包含
[root@m01 ~]# [[ $name =~ ^[a-z]+$ ]] && echo '包含' || echo '不包含'
包含
[root@m01 ~]# [[ $name =~ 'h'$ ]] && echo '包含' || echo '不包含'
不包含
[root@m01 ~]# [[ $name =~ 'g'$ ]] && echo '包含' || echo '不包含'
包含

逻辑表达式

1
2
3
!:非
&&:与 -a [[]] && [[]] [ $num -eq 0 -a $num2 -ne 3 ]
||:或 -o [[]] || [[]] [ $num -eq 0 -o $num2 -ne 3 ]

shell流程控制if

if条件语句语法

单分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if [ 条件1 ];then
动作1
动作2
动作3
fi

if [ 条件1 ]
then
动作1
动作2
动作3
fi

[ 条件1 ] && {
动作1
动作2
动作3
}

双分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if [ 条件1 ];then
动作1
动作2
动作3
else
动作1
动作2
动作3
fi

if [ 条件1 ]
then
动作1
动作2
动作3
else
动作1
动作2
动作3
fi

[ 条件1 ] && {
动作1
动作2
动作3
} || {
动作1
动作2
动作3
}

多分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if [ 条件1 ];then
动作1
动作2
动作3
elif [ 条件2 ];then
动作1
动作2
动作3
elif [ 条件3 ];then
动作1
动作2
动作3
else
动作1
动作2
动作3
fi

if [ 条件1 ]
then
动作1
动作2
动作3
elif [ 条件2 ]
then
动作1
动作2
动作3
elif [ 条件3 ]
then
动作1
动作2
动作3
else
动作1
动作2
动作3
fi

需求: 接收用户输入的用户名和密码进行登录。

  • 1.根据数据库中的数据判断,如果用户存在则验证密码
  • 2.如果密码错误则返回报错
  • 3.如果密码正确,则登录成功
  • 4.根据数据库中的数据判断,如果用户不存在则注册
    • 1)输入用户名
    • 2)输入密码
    • 3)确认密码
  • 5.如果密码不一致则重新输入
  • 6.如果密码一致,则将注册用户保存到数据库

扩展需求:

  • 判断用户输入的密码,如果小于8位,则报错
  • 判断用户输入的密码,如果没有大小写,则报错
  • 判断用户输入的密码,如果没有数字,则报错
  • 判断用户输入的用户名,如果用户名以数字开头,则报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
判断用户是否存在,bug,密码和要注册的用户名一致的情况下,就出问题

程序逻辑:
- 登录
输入用户名
输入密码
- 判断用户是否存在
如果用户存在,则验证密码
如果用户不存在,则跳转到注册页面
- 注册页面
需要输入用户名,也需要判断是否存在
如果存在,重新输入用户名
输入注册密码和确认密码
$pass1 == $pass2
$pass1 != $pass2
两次密码不一致,重新输入密码
两次密码一致,将用户写入数据库,跳转到登录页面

#!/bin/bash

read -p '请输入一个密码:' pass

if [ ${#pass} -lt 8 ];then
echo '密码至少8位'
elif [[ $pass =~ ' ' ]];then
echo '密码中不能包含空格'
elif [[ $pass =~ [0-9] ]] && [[ $pass =~ [a-z] ]] && [[ $pass =~ [A-Z] ]] ;then
echo '注册成功'
else
echo '密码要包含数字,大小写字母'
fi

###### 作业代码
[root@m01 ~]# cat login.sh
#!/bin/bash

. /etc/init.d/functions

user_data_file="/root/user_data.txt"

[ ! -f $user_data_file ] && {
touch $user_data_file
}

login_page (){
cat <<EOF
+------------------+
| 欢迎来到登录界面 |
+------------------+
EOF
}

register_page (){
cat <<EOF
+------------------+
| 欢迎来到注册界面 |
+------------------+
EOF
}
clear
login_page
read -p '用户名: ' user_name
read -p '密码: ' user_pass

[ ${#user_name} -eq 0 -o ${#user_pass} -eq 0 ] && {
echo -e "\e[1;31m Error:\e[0m \e[5;31m用户名和密码不能为空\e[0m"
exit 1
}

user_exists=`awk '{print $1}' $user_data_file | grep -w "$user_name"|wc -l &>/dev/null`
[ $user_exists -eq 0 ] && {
pass=`grep -w "$user_name" $user_data_file |awk '{print $2}'`
[ "$pass" == "$user_pass" ] && {
figlet welcome
action "$user_name login accessful" /bin/true
} || {
echo -e "\n--------- 登录失败 --------"
action "$user_name password is missing" /bin/false
exit 1
}
} || {
echo -e "\n--------- 登录失败 --------"
action "$user_name not exists" /bin/false
clear
register_page
read -p "请输入注册用户名:" register_name
read -p "请输入注册密码:" register_pass1
read -p "请确认注册密码:" register_pass2
grep -w "$register_name" $user_data_file &>/dev/null
[ $? -eq 0 ] && {
echo -e "\n--------- 注册失败 --------"
echo -e "\e[1;31m Error:\e[0m 用户 \e[5;31m[${register_name}]\e[0m 已存在"
} || {
[ $register_pass1 == $register_pass2 ] && {
echo "$register_name $register_pass1" >> $user_data_file
awk '{print $1}' $user_data_file | grep -w "$register_name" &>/dev/null
[ $? -eq 0 ] && {
echo -e "user \e[5;32m[$register_name]\e[0m register successful"
login_page
read -p '用户名: ' user_name
read -p '密码: ' user_pass
pass=`grep -w "$user_name" $user_data_file |awk '{print $2}'`
[ "$pass" == "$user_pass" ] && {
figlet welcome
action "$user_name login accessful" /bin/true
} || {
echo -e "\n--------- 登录失败 --------"
action "$user_name password is missing" /bin/false
exit 1
}
}
} || {
echo -e "\nthe password not match"
echo -e "\n--------- 重新确认密码 --------"
read -p "请输入注册密码:" register_pass1
read -p "请确认注册密码:" register_pass2
[ $register_pass1 == $register_pass2 ] && {
echo "$register_name $register_pass1" >> $user_data_file
}
}
}
}

shell端口扫描

企业中常用的监控命令

监控目标 命令
本地端口监控 netstat -lntup
ss -lntup
lsof
telnet
远端监控 nc
nmap
进程监控 ps -ef
ps -aux
web监控 curl
wget
文件内容 md5

端口检查

本地端口检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
## 错误方式
[root@m01 ~]# netstat -lntup|grep '22'
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6914/sshd
tcp6 0 0 :::22 :::* LISTEN 6914/sshd

## netstat 正确方式
[root@m01 ~]# netstat -lntup|grep [s]shd
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6914/sshd
tcp6 0 0 :::22 :::* LISTEN 6914/sshd
[root@m01 ~]# netstat -lntup|grep -w '22'
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 6914/sshd
tcp6 0 0 :::22 :::* LISTEN 6914/sshd
[root@m01 ~]# netstat -lntup|grep -w '22' &>/dev/null
[root@m01 ~]# echo $?
0
[root@m01 ~]# netstat -lntup|grep -w '10050' &>/dev/null
[root@m01 ~]# echo $?
1
[root@m01 ~]# netstat -lntup|grep -w '22'|wc -l
2
[root@m01 ~]# netstat -lntup|grep -w '10050'|wc -l
0

## ss
[root@m01 ~]# ss -lntup|grep 22
tcp LISTEN 0 128 *:22 *:* users:(("sshd",pid=6914,fd=3))
tcp LISTEN 0 128 :::22 :::* users:(("sshd",pid=6914,fd=4))
[root@m01 ~]# ss -lntup|grep -w 22
tcp LISTEN 0 128 *:22 *:* users:(("sshd",pid=6914,fd=3))
tcp LISTEN 0 128 :::22 :::* users:(("sshd",pid=6914,fd=4))

## lsof
[root@m01 ~]# lsof -i:22
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 6914 root 3u IPv4 37631 0t0 TCP *:ssh (LISTEN)
sshd 6914 root 4u IPv6 37633 0t0 TCP *:ssh (LISTEN)
sshd 7002 root 3u IPv4 38381 0t0 TCP m01:ssh->10.0.0.1:58616 (ESTABLISHED)
sshd 19974 root 3u IPv4 202068 0t0 TCP m01:ssh->10.0.0.1:58531 (ESTABLISHED)

使用脚本判断远程端口是否存活

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
## telnet
#!/bin/bash

IP=$1
port_count=`echo ''|telnet 172.16.1.7 80 2>/dev/null |grep 'Connected'|wc -l`

if [ $port_count -eq 0 ];then
echo '端口不存活'
else
echo '端口存活'
fi

## 使用telnet端口扫描
#!/bin/bash

. /etc/init.d/functions

IP=$1
for port in `seq 65535`;do
{
port_count=`echo ''|telnet $IP $port 2>/dev/null |grep 'Connected'|wc -l`

if [ $port_count -ne 0 ];then
action "$port 端口" /bin/true
fi
} &
done

## nc 网络中的瑞士军刀
[root@m01 ~]# echo ''|nc 172.16.1.7 80
Ncat: Connection refused.
[root@m01 ~]# echo $?
127
[root@m01 ~]# echo ''|nc 172.16.1.7 80
[root@m01 ~]# echo $?
0

# nc选项
-l:开启一个指定的端口
-k:保持端口持续连接
-u:指定nc使用udp协议(默认tcp)
-s:指定发送数据的源IP地址,适用于多网卡机器
-w:设置超时时间
-z:扫描时不发送任何数据

## nmap
# 单个IP扫描
[root@m01 ~]# nmap 172.16.1.7

# 单个IP的单个端口扫描
[root@m01 ~]# nmap -p 22 172.16.1.7

# 单个IP的范围端口扫描
[root@m01 ~]# nmap -p 1-65535 172.16.1.7

# 多个IP的范围端口扫描
[root@m01 ~]# nmap -p 1-65535 172.16.1.7 driverzeng.com

进程判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@web01 ~]# ps -ef|grep [n]ginx
root 12209 1 0 10:42 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
www 12210 12209 0 10:42 ? 00:00:00 nginx: worker process
[root@web01 ~]# ps -ef|grep [n]ginx|wc -l
2
[root@web01 ~]# ps -ef|grep [n]ginx1111|wc -l
0

## 远程进程检测
vim check_process.sh
#!/bin/bash

proc_count=`ssh 172.16.1.7 'ps -ef|grep [n]ginx|wc -l'`

if [ $proc_count -eq 0 ];then
echo 'nginx不存活'
else
echo 'nginx存活'
fi

网站检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl选项
-I:获取主机响应头部信息
-s:取消默认输出
-o:保存下载页面内容
-w:获取状态码
-u:身份认证 -u 用户名:密码
-H:添加请求头部信息
-v:显示详细信息
-L:跟随跳转
-X:指定请求方式
-A:修改用户的客户端

[root@m01 ~]# curl -uroger:roger -s -w "%{http_code}" -o /dev/null blog.roger.com
[root@m01 ~]# curl -s -w "%{http_code}" -o /dev/null http://roger:roger@blog.roger.com
[root@m01 ~]# curl -v http://www.360buy.com -L

wget选项
-O:保存下载页面内容
-r:递归下载
--debug:显示访问的详细过程 类似 curl -v
-q:静默输出 类似 curl -s
--spider:只查看不下载

文件检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@m01 ~]# md5sum 1.txt 
6de9439834c9147569741d3c9c9fc010 1.txt
[root@m01 ~]# md5sum 1.txt > /tmp/check_1.txt
[root@m01 ~]# md5sum -c /tmp/check_1.txt
1.txt: OK
[root@m01 ~]# md5sum -c /tmp/check_1.txt
1.txt: OK
[root@m01 ~]# md5sum -c /tmp/check_1.txt
1.txt: OK
[root@m01 ~]# md5sum -c /tmp/check_1.txt
1.txt: OK
[root@m01 ~]# echo 111 > 1.txt
[root@m01 ~]# md5sum -c /tmp/check_1.txt
1.txt: FAILED
md5sum: WARNING: 1 computed checksum did NOT match

shell编程函数

函数介绍

函数就是具备某一个功能的工具

为什么要使用函数

1
2
3
4
如果不使用函数,那么你的代码:
1.程序的组织结构不清晰,可读性差
2.代码冗余
3.可扩展性(功能需要修改的时候...对不起GG)

如何使用函数

1
2
3
函数的使用必须遵循的原则:先定义,后调用
修理工事先准备好工具的过程,即,定义函数
修理工遇到应用场景拿来工具就用即函数的调用

函数语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function 函数名 () {
命令1
命令2
命令3
}

function 函数名 {
命令1
命令2
命令3
}

函数名 () {
命令1
命令2
命令3
}

:() {
: | : &
};:

roger(){
roger | roger &
};roger

ls ; ifocnfig

## 函数的定义和调用
[root@m01 ~]# vim function.sh
#!/bin/bash

roger(){
xxx
}

xxx(){
echo 123
}
roger

## 错误的调用方式
[root@m01 ~]# vim function.sh
#!/bin/bash

roger(){
xxx
}
roger

xxx(){
echo 123
}

## 函数的位置变量
[root@m01 ~]# !vim
vim function.sh
#!/bin/bash

name=$1
age=$2
roger(){
echo $1
echo $2
}

roger $1 xxx

函数位置变量VS脚本位置变量

特殊变量 脚本 函数
$N 脚本的第N个参数 函数的第N个参数
$0 脚本名称 脚本名称
$*/$@ 脚本的所有参数 脚本的所有参数
$# 脚本传递的参数个数 函数传递的参数个数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@m01 ~]# !vim
vim function.sh
#!/bin/bash

name=$1
age=$2
var=$*
echo "函数外 $0"
roger(){
echo $1
echo $2
echo "函数内 $0"
echo $*
echo $#
}

roger abc xxx

函数的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[root@m01 ~]# cat function.sh
#!/bin/bash

. /etc/init.d/functions
domain_name_list=(www.roger.com blog.roger.com php.roger.com)
IP_list=(10.0.0.61 10.0.0.7)
proc_count=`ps -ef|grep [n]ginx|wc -l`
port_80_count=`netstat -lntup|grep -w '80'|wc -l`
port_443_count=`netstat -lntup|grep -w '443'|wc -l`

check(){
for domain_name in ${domain_name_list[*]};do
http_code=`curl -s -w "%{http_code}" -o /dev/null $domain_name`
if [ $http_code -eq 401 ];then
action "${domain_name}网站正常,但是身份验证不通过" /bin/false
elif [[ $http_code =~ ^[4-5] ]];then
action "${domain_name}网站无法访问" /bin/false
elif [ $proc_count -le 0 ];then
action "nginx进程" /bin/false
elif [ $port_80_count -le 0 ];then
action "nginx的80端口检测" /bin/false
#elif [ $port_443_count -le 0 ];then
# echo 'nginx的443端口不存在'
else
action "${domain_name}网站" /bin/true
fi
done
if [ ];then
return 10
else
return 20
fi
}

check
if [ $? -eq 20 ];then
xxx
fi

## 函数的返回值
function.sh: line 27: return: ok: numeric argument required
函数返回值只接收,数字类型的参数

function.sh: line 27: return: too many arguments
函数的返回值,只能接收一个参数

作业:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

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

ansible -m file -a 'mode=0644,owner=root,path=/tmp,state=directory'
根据菜单,安装对应的架构
输入数字和 lnmp nginx

shell 循环

循环的分类

1
2
3
while:当型循环				应用场景:死循环、按行读取文件、有条件的循环
do until:直到型循环 应用场景:没有场景
for:通用型循环 应用场景:很多应用场景

循环语法

while循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
n=1
while [ $n -le 10 ];do
echo $n
((n++))
done

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

1+2+3+4+5+6+7+8+9+10+...+100
(1+100)*100/2=5050

1+2+3+...+9

使用while循环实现1加到100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

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

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

until循环(忘记它)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
until <条件表达式>;do
cmd1
cmd2
cmd3
done

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

#!/bin/bash

n=0
until [ $n -gt 10 ];do
echo $n
((n++))
done

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

for循环

语法一

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
for var in roger wyk huanglong;do
cmd1
cmd2
done

for var in 变量表达式
do
cmd1
cmd2
done

变量表达式:
- 可以是文件中的内容(按空格循环)
- 可以是序列命令的内容
- 可以是数组中的内容
- 可以是以空格为分隔符的字符串

## 文件内容
for var in `cat 1.txt`;do
echo $var
done

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

## 序列内容
for var in `seq 10`;do
echo $var
done

## 数组
[root@m01 ~]# vim a.sh
#!/bin/bash
array=(roger wyk huanglong)
for var in ${array[*]};do
echo $var
done

## 以空格为分割符的字符串
name="roger wyk huanglong"
for var in $name;do
echo $var
done

语法二

指定循环次数

1
2
3
for((i=1;i<=10;i++));do
echo $i
done

死循环

1
2
3
for (( ; ; ));do
echo 123
done

循环的控制语句

1
2
-continue
-break

break跳出循环

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

while true;do
read -p 'Please Input A Number: ' num
if [ $num -ne 5 ];then
echo "你输入的是 $num"
else
break
fi
done

#!/bin/bash

n=0
while [ $n -lt 10 ];do
((n++))
if [ $n -eq 5 ];then
break
fi
echo $n
done

continue 跳出循环

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

while true;do
read -p 'Please Input A Number: ' num
if [ $num -ne 5 ];then
echo "你输入的是 $num"
else
continue
fi
done

#!/bin/bash

n=0
while [ $n -lt 10 ];do
((n++))
if [ $n -eq 5 ];then
continue
fi
echo $n
done

while循环读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
## for循环读取文件
[root@m01 ~]# cat 1.txt
www.roger.com wyk
blog.roger.com huanglong
php.roger.com wujiahao

[root@m01 ~]# cat a.sh
#!/bin/bash

for word in `cat 1.txt`;do
echo $word
done

## while读取文件
# 方法一:
#!/bin/bash

exec < 1.txt
while read line;do
echo '---------'
echo $line
done

# 方法二:
#!/bin/bash
cat 1.txt|while read line;do
echo '---------'
echo $line
done

# 方法三:
#!/bin/bash

while read line;do
echo '---------'
echo $line
done < 1.txt

while练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
写一个脚本,读取下面文件内容,并算出所有人年龄总和
[root@zabbix01 ~]# cat student.txt
曾老湿 18
苍井空 20
武藤兰 33
天海翼 32
西野翔 18

## 方法一:
#!/bin/bash

sum=0
while read line;do
age=`echo $line|awk '{print $2}'`
((sum+=age))
done < 1.txt
echo $sum

## 方法二:
#!/bin/bash

sum=0
exec < 1.txt
while read line;do
age=`echo $line|awk '{print $2}'`
((sum+=age))
done < 1.txt
echo $sum

案例

案例一:

日志分析案例

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

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

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

案例二:

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

生成随机内容的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
## 方法一:
[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

案例三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 提示一:变量子串方法

# 提示二:sed awk方法

# 提示三:命令拼接

# 提示四:rename
rename roger.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个系统用户,生成随机密码

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

用户名:密码

案例五:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

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

figlet

1
2
3
4
[root@zabbix01 ~]# figlet roger
[root@zabbix01 ~]# showfigfonts
[root@zabbix01 ~]# figlet roger -f banner
[root@zabbix01 ~]# watch -n1 "date +%D%n%T|figlet -k"

shell case语句

写一个跳板机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash

cat <<EOF
+------------------------+
| 1 172.16.1.7 web01 |
+------------------------+
| 2 172.16.1.8 web02 |
+------------------------+
| 3 172.16.1.31 nfs |
+------------------------+
| 4 172.16.1.41 backup |
+------------------------+
| 5 172.16.1.51 db01 |
+------------------------+
| 6 172.16.1.52 db02 |
+------------------------+
| 7 172.16.1.53 db03 |
+------------------------+
| 8 172.16.1.54 db04 |
+------------------------+
| 9 172.16.1.61 m01 |
+------------------------+
| 10 172.16.1.71 zabbix |
+------------------------+
EOF

read -p '请输入需要连接的主机:' host

if [ $host == '1' -o $host == '172.16.1.7' -o $host == 'web01' ];then
#ssh 172.16.1.7
echo '连接 172.16.1.7 web01'
elif [ $host == '2' -o $host == '172.16.1.8' -o $host == 'web02' ];then
echo '连接 172.16.1.8 web02'
elif [ $host == '3' -o $host == '172.16.1.31' -o $host == 'nfs' ];then
echo '连接 172.16.1.31 nfs'
elif [ $host == '4' -o $host == '172.16.1.41' -o $host == 'backup' ];then
echo '连接 172.16.1.41 backup'
elif [ $host == '5' -o $host == '172.16.1.51' -o $host == 'db01' ];then
echo '连接 172.16.1.51 db01'
elif [ $host == '6' -o $host == '172.16.1.52' -o $host == 'db02' ];then
echo '连接 172.16.1.52 db02'
elif [ $host == '7' -o $host == '172.16.1.53' -o $host == 'db03' ];then
echo '连接 172.16.1.53 db03'
elif [ $host == '8' -o $host == '172.16.1.54' -o $host == 'db04' ];then
echo '连接 172.16.1.54 db04'
elif [ $host == '9' -o $host == '172.16.1.61' -o $host == 'm01' ];then
echo '连接 172.16.1.61 m01'
elif [ $host == '10' -o $host == '172.16.1.71' -o $host == 'zabbix' ];then
echo '连接 172.16.1.71 zabbix'
fi

case语句

case … esac 为多选择语句,与其他语言中的 switch … case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case … esac 语句,esac(就是 case 反过来)作为结束标记。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 语法:
case 变量 in
动作1|动作3|动作4)
cmd1
;;
动作2)
cmd2
*)
echo '脚本的用法'
;;
esac

case会将变量和动作进行判断
可以进行多动作判断,使用 '|'

给shell加颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
## 固定格式
\e[1;31mroger\e[0m

red_start='\e[1;31m'
red_end='\e[0m'

$red_start xxx $red_end

echo -e '\e[1;31mroger\e[0m'

[root@m01 ~]# echo -e '\e[5;32;41m roger \e[0m'

\e \033 \E
字体型号;字体颜色;背景颜色m

## 字体型号:
0:正常字体
1:字体加粗
2:字体变浅
3:字体斜体
4:字体下划线
5:字体闪烁

## 字体颜色
30m:黑色
31m:红色
32m:绿色
33m:黄色
34m:蓝色
35m:紫色
36m:天蓝
37m:白色

## 背景颜色
40m:黑色
41m:红色
42m:绿色
43m:黄色
44m:蓝色
45m:紫色
46m:天蓝
47m:白色

正则表达式

正则表达式,简写:re,全拼:(regular expression) 在某些地区,管它叫做,正规表达式、规则表达式

正则表达式的”祖先”可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。

1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为”神经网事

件的表示法”的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为”正则集的代数”的表达式,因此采用”正则表达式”这个术语。

随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson 是 Unix的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。

如他们所说,剩下的就是众所周知的历史了。从那时起直至现在正则表达式都是基于文本的编辑器和搜索工具中的一个重要部分。

正则和通配符

1
2
3
4
5
6
7
# 通配符
## 跟下所有文件
ls /*

# 正则
*
前面的字符出现0次或者多次

为什么使用正则

1.方便处理文本和字符串内容

2.处理有规律的内容(手机号,身份证等等有规律的、固定写法的内容)

3.正则一般给高级的开发语言使用

  • python

  • GO

  • C++

  • JAVA

awk和sed也是一门语言

搜索和替换操作 但是一般的命令,搜索和替换缺乏灵活性,基本写死。 所以我们可以通过正则表达式,灵活的动态

匹配文本。

例如:

1.可以测试输入字符串 以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证。

2.替换文本。 可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。

3.基于模式匹配从字符串中提取子字符串。

4.查找文档内或输入域内特定的文本。

正则表达式在Linux中的分类

在正则表达式的语法中,主要有两个部分 修饰符 和 元字符 。 修饰符,我们在后面介绍,它主要不写在正则中,要写在正则的外面。 元字符,在Linux中会把这些符号区分开,划分为 基础正则 和 扩展正则

所谓的 扩展正则 其实也是元字符中的一部分,只不过在linux中,有些命令不能直接使用某些元字符,需要用一些参

数,才能使用。所以被一部分人称之为 扩展正则

那么我们先简单介绍一下,这个分类,然后再总的来介绍所有修饰符和元字符。

1
2
3
# 开发里,回车和换行符是两码事
\r 回车
\n 换行

在Linux中的分类

  • 修饰符:它主要不写在正则中,要写在正则的外面

  • 元字符:在Linux中会把这些符号区分开

    • 基础正则
    • 扩展正则

基础正则 简写bre(basicc regular expression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
^ 以什么什么开头
$ 以什么什么结尾
. 匹配任意字符
* 前面的字符出现0次或者多次
.* 配合使用

[] 字符集合, 匹配多包含的任意一个字符
例如:
[0-9]
[a-z]

[^] 字符集合的取反
例如:
^[0-9]
^[a-z]

拓展正则 简写ere(extended rugular expression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 比较常见的
+ 前面的一个字符出现一次或者多次
cat 1.txt | awk -F '[ :]+' '{print $5}'

## 贪婪匹配
[ :]+ 里面的任意字符出现一次或者多次
即使是这两个一起出现也会识别成一个

| 或者的意思
() 被括起来的内容提供看做是一个整体
也可以在sed命令中后项引用
{} 中间写一个非负整数,表示大括号前面的内容,出现指定次数
09:38:41 root@web01:~ # grep -E 'z{3}' ## 匹配包含3次的所有
z
zz
zzz
zzz
zzzz
zzzz
09:39:45 root@web01:~ # grep -E 'z{1,3}' ## 匹配只要包含z的所有
z
z
z
z
zz
zz
zzz
zzz
zxc
zxc
asdz
asdz

?: 前面的内容,出现0次或者1次
grep -E 'z?' 出现0次也行
grep -E 'z+' 最少出现一次

比如匹配id
[0-9].*
[0-9]+

正则表达式使用误区

通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
*代表所有内容
{}生成序列
echo {1..10}
echo {1..10..2}
cp /usr/lib/systemd/system/nginx.service{,.bak} ## 可以给文件做备份
cp /usr/lib/systemd/system/{nginx,zzz}.service

?匹配任意一个字符
touch check_1.txt
ll
ls -l /tmp/c?heck.txt
ls -l /tmp/c?eck.txt
ls -l /tmp/c?eck_1.txt
ls -l /tmp/c?eck_?????
ls -l /tmp/c?eck_?
ls -l /tmp/c?eck_?*
ls -l /tmp/???????????
ll
touch 1234
ls -l /tmp/????
ls -l /tmp/checl_?????
ls -l /tmp/check_?????
ls -l /tmp/c????_?????

[] 和[^] 与正则表达式中的用法一样

正则表达式使用注意事项

1.所有符号皆为英文符号

2.使用三剑客时加引号

3.注意字符集,如果出现字符集问题,那么将字符集修改为C(小概率事件)

4.像素眼(空格,换行符,tab键)

5.测试的时候,推荐使用grep -E或者egrep,因为过滤出来的内容会加颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# cat 1.txt
check10.txt
check1.txt
check 2.txt
check 3.txt
check4.txt
check5.txt
check6.txt
check7.txt
check8.txt
check9.txt
cat 1.txt | awk '/\t/{print}' ###过滤TAB的
check 3.txt
cat 1.txt | awk '/ /{print}' ###过滤空格的
check 2.txt

如果要筛选10:
10:20:53 root@web01:~ # cat 2.txt | awk '/[0-9]{2}/{print}'
check10.txt

10:19:45 root@web01:~ # cat 2.txt | awk '/[0-9][0-9]/{print}'
check10.txt
1
2
3
4
.* 匹配任意字符  ### 匹配所有
grep -o '.*'
. 匹配任意字符 ### 匹配单个
grep -o '.'

正则表达式的修饰符

1
2
3
s#匹配内容#要替换的内容#g

sed -i 's#查找的内容#替换的内容#g'

正则表达式常用的修饰符

修饰符 含义 描述
i gnore-不区分大小写 将匹配设置为不区分大小写,搜索时不区分大小写:A和a没有区别。
g global-全局匹配 查找所有的匹配项
m mu ltiline -多行匹配 使边界字符^和$匹配每一行的开头和结尾,记住是多行,而不是整个字 符串的开头和结尾
S 特殊字符圆点.中包含 换行符\n 默认情况下的圆点.是匹配除换行符\n之外的任何字符,加上s修饰符 之后,中包含换行符\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/pattern/fiags
/元字符/修饰符

11:36:17 root@web01:~ # cat a.txt
check_0.txt
check_10.tx
check_1.txt
check_2.txt
check_3.txt
check_4.txt
check _5.txt
check _6.txt
check_7.txt
check_8.txt
check_9.txt
CHECK.txt

0 ✓ 11:37:43 root@web01:~ # awk '/[a-z]{5}_[0-9]+.txt/i{print}' a.txt ### 加i
check_0.txt
check_10.tx
check_1.txt
check_2.txt
check_3.txt
check_4.txt
check _5.txt
check _6.txt
check_7.txt
check_8.txt
check_9.txt
CHECK.txt

正则表达式元字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
grep -帮助
匹配模式选择:
-E -extended-regexp 扩展正则表达式egrep
-F --fixed-strings一个换行符分隔的字符串的集合fgrep
-G -basic-regexp基本正则
-P -perl-regexp调用的实际抽取与汇报语言正则
-e --regexp=PATTERN后面根正则模式,默认无
-f -file=FILE 从文件中获得匹配模式
-i -ignore-case 不区分大小写
-w --word-regexp 匹配整个单词
-x --line-regexp匹配整行
-z -null-data一个O字节的数据行,但不是空行

12:10:24 root@web01:~ # grep -E 'zl+' ### + 1次或多次, 至少要1次
zls
zls
zlllls
zlllls
zs

12:12:08 root@web01:~ # grep -E 'zl*' ### * 0次或多次, 可以为0
zls
zls
zllls
zllls
zs
zs

130 ✗ 12:13:21 root@web01:~ # grep -E 'zl?' ### ? 0次或1次, 没有多次
zls
zls
zllls
zllls
zs
zs

正则案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#1.匹配身份证号 
#2.匹配手机号
#3.后项引用
#4.给用户传参中间加上空格(后项引用)
echo 123456
sed -r 's#(.*)#<\1>#g'
sed -r 's#(.)#<\1>#g '

echo {1..10} 带空格的每个都加上<>

#5.过滤IP
#6.过滤文件中,oldboy和oldbey的内容
#7.统计上面文件的单词数量
#8.统计上面文件的字母数量
#9.取出下面的指定内容
19:09:03 up 735 day,23:12,2 users,load average: 0.00,0.03,0.05
19:09:03 up 10:20, 3 users,load average: 0.00,0.02,0.05
19:09:03 up 0 min,1 users,load average: 0.31,0.08,0.03
#如何截取上面的登录用户数?
#如何截取上面的开机时间?

##因为在Linux中,我们目前只能使用awk grep sed取,而且支持的正则也是基础正则和扩展正则
##但是有些元字符,基础和扩展正则也不支持,我们只能使用其他语言的正则,比如python
##于是,在grep命令中,提供了一个选项叫做-p这个选项的作用就是,使用perl语言的正则
uptime| grep -Po '[0-9]+(?= user)'
uptime| grep -Po '\d+(?=user)'
uptimel grep -Po '(?< =up).*(?=[0-9] user)'
uptime| grep -Po '(?< =up).*(?=\d user)'

零宽断言 截取网卡IP的案例

1
2
3
4
5
6
7
8
9
ifconfig eth0|grep -Po '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(?=  netmask)'

12:53:14 root@web01:~ # ifconfig eth0|grep -Po '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(?= netmask)'
10.0.0.7

ifconfig eth0|grep -Po '[0-9]{1,3}\..+(?= netmask)'

12:54:32 root@web01:~ # ifconfig eth0|grep -Po '[0-9]{1,3}\..+(?= netmask)'
10.0.0.7

基础正则表BRE

符号 描述 应用场景
A 以 … 开头 匹配以指定字符开头的内容:^zls
$ 以 … 结尾 匹配以指定字符结尾的内容:zls$
. 匹配除换行符(\In、\r)之外的任何单个字符 一般该元字符不单独用,配合*一起使用
* 前一个字符连续出现0次或多次 zl*能匹配”z”以及”zl”,配合.使用要注意贪婪性
[ ] 字符集合,匹配所包含的任意一个字符 1.”[xyz]’可以匹配”zls”中的’z’
2.匹配数字[0-9]
3.小写字母[a-z]
4.大写字母[A-Z]
5.大小写都匹配[a-z][A-z]或者[a-Z]
6.在中括号中可以让特殊符号失去特殊含义
7.上面的大前提是^不能放第一个
[^] 反值字符集合,匹配未包含的任意字符 1. [^xyz]可以匹配”zls”中的’s’
2.匹配数字和
3.取出/etc/passwd第一列
\
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符
\n 匹配一个换行符 等价于\x0a和\cJ
\r 匹配一个回车符(回车键) 等价于\x0d和\cM
\t 匹配一个制表符(Tab键) 等价于\x09和\cl
\v 匹配一个垂直制表符 等价于\x0b和\cK
\f 换页符 等价于\x0c和\cL
\b 匹配一个单词边界,也就是指单词和空格间的位置 er\b’可以匹配”never”中的’er’,但不能匹配”verb” 中的’er’
\B 匹配非单词边界 er\B’能匹配”verb”中的’er’,但不能匹配”never” 中的’er
\d 匹配一个数字字符 等价于[0-9]
\D 匹配一个非数字字符 等价于 [^0-9]
\w 匹配字母、数字、下划线 等价于[A-Za-z0-9 ]
W 匹配非字母、数字、下划线 等价于[^A-Za-z0-9 ]
\s 匹配任何空白字符,包括空格、制表符、 换页符等等 等价于[\f\n\t\v]
\S 匹配任何非空白字符 等价于[^\f\n\r\t\v]

扩展正则表ERE

| 或 者 等价于[^\f\n\r\t\v]
前一个字符出现一次或者多次 zl+’能匹配”zl”以及”zll”,但不能匹配”z”,+等价于 {1,}
{n} n是一个非负整数。匹配确定的n次。 o{2}’不能匹配”bo3”中的’o’
但是能匹配”foot”中的两个o
{n,} n是一个非负整数。匹配确定的n次。 o{2,}’不能匹配”Bob”中的’o’
但能匹配”foooood”中的所有o
o{1,}’等价于’o+’
o{0,}’则等价于’o*’
{n,m} m和n均为非负整数,其中n<=m,最少匹配n 次且最多匹配m次 “o{1,3}”将匹配”fooooood”中的前三个o
o{0,1}’等价于’o?’
请注意在逗号和两个数之间不能有空格
() 1.被括起来的内容看做是一个整体 2.在sed命令中做后向引用 sed -nr’s#(.*)abc#\1#gp’
(?=pattern) 正向肯定预查look ahead(零宽断言) 使用grep-P来使用下面我们在Per语言正则中使用
(?<=pattern) 反肯定预查look behind(零宽断言) 使用grep-P来使用下面我们在Perl语言正则中使用
? 匹配前一个字符出现0次或1次 ‘do(es)?”可以匹配”do”或”does”。?等价于{0,1}

正则支持表

字符 说明 Basic RegEx
基础正则
Extended RegEx
扩展正则
python RegEx
python正则
Perl regEx
Perl正则
转义 \ \ \ \
^ 匹配行首,例如’^dog’匹配以字符串dog开头的行(注意:awk 指令中,’^’则是匹配字符串的开始) ^ ^ ^ ^
$ 匹配行尾,例如:’^、dog匹配以字符串为结尾的行(注意:指令中,’则是匹配字符串的结尾) $ $ $ $
^$ 匹配空行 ^$ ^$ ^$ ^$
^string$ 匹配行,例如:’^dog$’匹配只含一个字符串 dog 的行 ^string$ ^string$ ^string$ ^string$
< 匹配单词,例如:’<frog’ (等价于’\bfrog’),匹配以 frog 开头的单词 < < 不支持 不支持(但可以使用\b来匹配单词,例如:’\bfrog’)
> 匹配单词,例如:’frog>’(等价于’frog\b ‘),匹配以 frog 结尾的单词 > > 不支持 不支持(但可以使用\b来匹配单词,例如:’frog\b’)
匹配一个单词或者一个特定字符,例如:’‘(等价于’\bfrog\b’)、’ 不支持 不支持(但可以使用\b来匹配单词,例如:’\bfrog\b’
() 匹配表达式,例如:不支持’(frog)’ 不支持(但可以使用,如:dogdog () () ()
匹配表达式,例如:不支持’(frog)’ 不支持(同()) 不支持(同()) 不支持(同())
匹配前面的子表达式 0 次或 1 次(等价于{0,1}),例如:where(is)?能匹配”where” 以及”whereis” 不支持(同?)
? 匹配前面的子表达式 0 次或 1 次(等价于’{0,1}’),例如:’whereisis? ‘能匹配 “where”以及”whereis” ? 不支持(同?) 不支持(同?) 不支持(同?)
? 当该字符紧跟在任何一个其他限制符(*, +, ?, {n},{n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,’o+?’ 将匹配单个”o”,而 ‘o+’ 将匹配所有 ‘o’ 不支持 不支持 不支持 不支持
. 匹配除换行符(’\n’)之外的任意单个字符(注意:awk 指令中的句点能匹配换行符) . .(如果要匹配包括“\n”在内的任何一个字符,请使用:’(^$)|(.) . .(如果要匹配包括“\n”在内的任何一个字符,请使用:’ [.\n] ‘
* 匹配前面的子表达式 0 次或多次(等价于{0, }),例如:zo* 能匹配 “z”以及 “zoo” * * * *
+ 匹配前面的子表达式 1 次或多次(等价于’{1, }’),例如:’whereisis+ ‘能匹配 “whereis”以及”whereisis” + 不支持(同+) 不支持(同+) 不支持(同+)
+ 匹配前面的子表达式 1 次或多次(等价于{1, }),例如:zo+能匹配 “zo”以及 “zoo”,但不能匹配 “z” 不支持(同+) + + +
{n} n 必须是一个 0 或者正整数,匹配子表达式 n 次,例如:zo{2}能匹配 不支持(同{n}) {n} {n} {n}
{n,} “zooz”,但不能匹配 “Bob”n 必须是一个 0 或者正整数,匹配子表达式大于等于 n次,例如:go{2,} 不支持(同{n,}) {n,} {n,} {n,}
{n,m} 能匹配 “good”,但不能匹配 godm 和 n 均为非负整数,其中 n <= m,最少匹配 n 次且最多匹配 m 次 ,例如:o{1,3}将配”fooooood” 中的前三个 o(请注意在逗号和两个数之间不能有空格) 不支持(同{n,m}) {n,m} {n,m} {n,m}
x|y 匹配 x 或 y,例如: 不支持’z|(food)’ 能匹配 “z” 或”food”;’(z|f)ood’ 则匹配”zood” 或 “food” 不支持(同x|y) x|y x|y x|y
[0-9] 匹配从 0 到 9 中的任意一个数字字符(注意:要写成递增) [0-9] [0-9] [0-9] [0-9]
[xyz] 字符集合,匹配所包含的任意一个字符,例如:’[abc]’可以匹配”lay” 中的 ‘a’(注意:如果元字符,例如:. *等,它们被放在[ ]中,那么它们将变成一个普通字符) [xyz] [xyz] [xyz] [xyz]
[^xyz] 负值字符集合,匹配未包含的任意一个字符(注意:不包括换行符),例如:’[^abc]’ 可以匹配 “Lay” 中的’L’(注意:[^xyz]在awk 指令中则是匹配未包含的任意一个字符+换行符) [^xyz] [^xyz] [^xyz] [^xyz]
[A-Za-z] 匹配大写字母或者小写字母中的任意一个字符(注意:要写成递增) [A-Za-z] [A-Za-z] [A-Za-z] [A-Za-z]
[^A-Za-z] 匹配除了大写与小写字母之外的任意一个字符(注意:写成递增) [^A-Za-z] [^A-Za-z] [^A-Za-z] [^A-Za-z]
\d 匹配从 0 到 9 中的任意一个数字字符(等价于 [0-9]) 不支持 不支持 \d \d
\D 匹配非数字字符(等价于 [^0-9]) 不支持 不支持 \D \D
\S 匹配任何非空白字符(等价于[^\f\n\r\t\v]) 不支持 不支持 \S \S
\s 匹配任何空白字符,包括空格、制表符、换页符等等(等价于[ \f\n\r\t\v]) 不支持 不支持 \s \s
\W 匹配任何非单词字符 (等价于[^A-Za-z0-9_]) \W \W \W \W
\w 匹配包括下划线的任何单词字符(等价于[A-Za-z0-9_]) \w \w \w \w
\B 匹配非单词边界,例如:’er\B’ 能匹配 “verb” 中的’er’,但不能匹配”never” 中的’er’ \B \B \B \B
\b 匹配一个单词边界,也就是指单词和空格间的位置,例如: ‘er\b’ 可以匹配”never” 中的 ‘er’,但不能匹配 “verb” 中的’er’ \b \b \b \b
\t 匹配一个横向制表符(等价于 \x09和 \cI) 不支持 不支持 \t \t
\v 匹配一个垂直制表符(等价于 \x0b和 \cK) 不支持 不支持 \v \v
\n 匹配一个换行符(等价于 \x0a 和\cJ) 不支持 不支持 \n \n
\f 匹配一个换页符(等价于\x0c 和\cL) 不支持 不支持 \f \f
\r 匹配一个回车符(等价于 \x0d 和\cM) 不支持 不支持 \r \r
\ 匹配转义字符本身”” \ \ \ \
\cx 匹配由 x 指明的控制字符,例如:\cM匹配一个Control-M 或回车符,x 的值必须为A-Z 或 a-z 之一,否则,将 c 视为一个原义的 ‘c’ 字符 不支持 不支持 \cx
\xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长,例如:’\x41’ 匹配 “A”。’\x041’ 则等价于’\x04’ & “1”。正则表达式中可以使用 ASCII 编码 不支持 不支持 \xn
\num 匹配 num,其中 num是一个正整数。表示对所获取的匹配的引用 不支持 \num \num
[:alnum:] 匹配任何一个字母或数字([A-Za-z0-9]),例如:’[[:alnum:]] ‘ [:alnum:] [:alnum:] [:alnum:] [:alnum:]
[:alpha:] 匹配任何一个字母([A-Za-z]), 例如:’ [[:alpha:]] ‘ [:alpha:] [:alpha:] [:alpha:] [:alpha:]
[:digit:] 匹配任何一个数字([0-9]),例如:’[[:digit:]] ‘ [:digit:] [:digit:] [:digit:] [:digit:]
[:lower:] 匹配任何一个小写字母([a-z]), 例如:’ [[:lower:]] ‘ [:lower:] [:lower:] [:lower:] [:lower:]
[:upper:] 匹配任何一个大写字母([A-Z]) [:upper:] [:upper:] [:upper:] [:upper:]
[:space:] 任何一个空白字符: 支持制表符、空格,例如:’ [[:space:]] ‘ [:space:] [:space:] [:space:] [:space:]
[:blank:] 空格和制表符(横向和纵向),例如:’[[:blank:]]’ó’[\s\t\v]’ [:blank:] [:blank:] [:blank:] [:blank:]
[:graph:] 任何一个可以看得见的且可以打印的字符(注意:不包括空格和换行符等),例如:’[[:graph:]] ‘ [:graph:] [:graph:] [:graph:] [:graph:]
[:print:] 任何一个可以打印的字符(注意:不包括:[:cntrl:]、字符串结束符’\0’、EOF 文件结束符(-1), 但包括空格符号),例如:’[[:print:]] ‘ [:print:] [:print:] [:print:] [:print:]
[:cntrl:] 任何一个控制字符(ASCII 字符集中的前 32 个字符,即:用十进制表示为从 0 到31,例如:换行符、制表符等等),例如:’ [[:cntrl:]]’ [:cntrl:] [:cntrl:] [:cntrl:] [:cntrl:]
[:punct:] 任何一个标点符号(不包括:[:alnum:]、[:cntrl:]、[:space:]这些字符集) [:punct:] [:punct:] [:punct:] [:punct:]
[:xdigit:] 任何一个十六进制数(即:0-9,a-f,A-F) [:xdigit:] [:xdigit:] [:xdigit:] [:xdigit:]

Linux三剑客

grep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-n:打印行号
-A:after 打印过滤内容的后N行
-B:before 打印过滤内容的前N行
-C:center 打印过滤内容的前后N行
-E:支持扩展正则 ere #grep -E 'root|nginx' /etc/passwd
-v:取反
-o:只打印匹配到的内容
-w:精确匹配
-P:支持Perl正则
-i:忽略大小写
-r:递归检索文件中的内容 #grep -r 'www.baidu.com' ./
-R:递归检索文件中的内容,包括软链接文件 #grep -R 'www.baidu.com' ./
-l:只显示文件名
-h:只显示文件内容
-f:对比文件内容,内容少的文件在前面,内容多的文件在后面,取反可以看到不同的文件内容
-c:统计行数,类似于 wc -l
-G:支持基础正则 bre
-m:显示前N行 类似于 head -n

[root@m01 web]# grep -r 'www.roger.com' /root/web/
/root/web/css/style.css:www.roger.com
/root/web/js/main.js:www.roger.com
/root/web/index.html:www.roger.com

[root@m01 web]# grep -lr 'www.roger.com' /root/web/|xargs sed -i.roger 's#www.roger.com#www.baidu.com#g'

sed

在sed中,我们核心的内容,主要分为四个部分:

当然我们还有一些进阶内容:模式空间与保持空间

sed命令执行流程

举个例子:

1
2
3
4
5
# 以下是roger.txt文件内容
1,roger,666
2,wls,777
3,cls,888
4,lls,999

执行 sed -n '3p' roger.txt命令后,sed都做了啥?

1.sed先是按行读取文件内容
2.每读取一行内容,都会进行一次判断,是不是你想要的行
3.如果不是,则判断是不是加了-n选项
4.如果加了-n,就读取下一行
5.如果没加-n,就会将所有内容输出到命令行(默认输出)
6.如果是,你想要的那一行(第三行)则判断执行的后续动作(p d s a i c)
7.动作处理完成后,输出指定的内容
8.即便是读取完了,内容也输出了,sed也会继续往后读,直到文件的最后一行

sed - 查

sed命令选项 选项含义 sed命令动作 动作含义
-n 取消默认输出 p print打印
-r 支持扩展正则 d delete删除
a append追加
i insert插入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
p:打印,显示

## sed显示单行内容
[root@m01 ~]# sed '3p' roger.txt
1,roger,666
2,wls,777
3,cls,888
3,cls,888
4,lls,999

## sed取消默认输出
[root@m01 ~]# sed -n '3p' roger.txt
3,cls,888

## sed显示多行内容并取消默认输出
[root@m01 ~]# sed -n '1,3p' roger.txt
1,roger,666
2,wls,777
3,cls,888

## sed模糊查询
[root@m01 ~]# sed -n '/roger/p' roger.txt
1,roger,666

[root@m01 ~]# sed -nr '/roger|cls/p' roger.txt
1,roger,666
3,cls,888

[root@m01 ~]# sed -n '/roger/,/cls/p' roger.txt
1,roger,666
2,wls,777
3,cls,888

## sed实现 grep -A
[root@m01 ~]# sed -n '/roger/,+2p' roger.txt
1,roger,666
2,wls,777
3,cls,888

[root@m01 ~]# grep 'roger' -A 2 roger.txt
1,roger,666
2,wls,777
3,cls,888

## sed 隔指定行数读取一行
[root@m01 ~]# sed -n '1~2p' roger.txt
1,roger,666
3,cls,888
[root@m01 ~]# sed -n '1~3p' roger.txt
1,roger,666
4,lls,999

sed - 删

1
2
3
4
5
6
7
8
9
10
11
12
d:delete 删除

## 删除指定行数,不修改原文件
[root@m01 ~]# sed '2d' roger.txt

## 删除最后一行
[root@m01 ~]# sed '$d' roger.txt
1,roger,666
3,cls,888

## 包含roger的行到cls的行都删掉
[root@m01 ~]# sed -n '/roger/,/cls/d' roger.txt

sed - 增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
c:replace 替换整行内容
[root@m01 ~]# cat roger.txt
1,roger,666
3,cls,888
4,lls,999
[root@m01 ~]# sed '2c2,huanglong,438' roger.txt
1,roger,666
2,huanglong,438
4,lls,999

a:append 追加

[root@m01 ~]# sed '$a5,huanglong,438' roger.txt
1,roger,666
3,cls,888
4,lls,999
5,huanglong,438
[root@m01 ~]# sed '2a5,huanglong,438' roger.txt
1,roger,666
3,cls,888
5,huanglong,438
4,lls,999
[root@m01 ~]# sed '1a2,huanglong,438' roger.txt
1,roger,666
2,huanglong,438
3,cls,888
4,lls,999

i:insert 插入

[root@m01 ~]# sed '$i2,huanglong,438' roger.txt
1,roger,666
3,cls,888
2,huanglong,438
4,lls,999
[root@m01 ~]# sed '1i2,huanglong,438' roger.txt
2,huanglong,438
1,roger,666
3,cls,888
4,lls,999

sed - 改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
s:substitute 替换
g:global 全局
s###g
s@@@g
s啥都行g

## 基础用法
[root@zabbix01 ~]# sed 's#roger#roger#g' roger.txt
1,roger,666
2,wls,777
3,cls,888
4,lls,999

## 使用正则
[root@zabbix01 ~]# sed 's#[0-9]#666#g' roger.txt
666,roger,666666666
666,wls,666666666
666,cls,666666666
666,lls,666666666

## 后向引用
[root@m01 ~]# ifconfig eth0|sed -nr 's#^.*inet (.*) net.*#\1#gp'
10.0.0.61
[root@m01 ~]# ip a s eth1|sed -nr 's#^.*inet (.*)/24.*#\1#gp'
172.16.1.61

sed的模式空间

将文件中的,所有换行符,替换成空格

1
N:在读取文件是,让sed把下一行内容一起读进去

awk

awk的内置变量和动作和选项

awk内置变量 变量含义 awk选项 选项含义 awk动作 动作含义
NR Number of Record 行号 -F 指定分隔符 gsub 替换
RS Record Separator 行的分隔符(\n) -v 指定变量(内置变量、自定义变量) print 打印
FS Field Separator 列的分隔符(空格)
NF Number Of Filed 每一行有多少列

注意:awk输出变量使用单引号,bash输出变量使用双引号

awk不是一个命令,是一门语言。

awk又叫做GNU awk,gawk

1
2
[root@m01 ~]# ls -l $(which awk)
lrwxrwxrwx. 1 root root 4 Jul 5 2021 /usr/bin/awk -> gawk

awk执行流程

三个阶段

  • 读取文件之前
    • BEGIN{}
    • 1.读取文件之前,先看命令的选项,例如 -F,-v
    • 2.如果写了BEGIN{}则先在BEGIN{}中的指令
  • 读取文件时
    • {}
    • 1.awk在读取文件时,也是一行一行的读\
    • 2.读取一行之后,判断是否满足条件,如果是,则执行{对应动作}
    • 3.如果不满足条件,awk继续读取下一行,直到满足条件或者到文件的最后一行
  • 读取文件之后
    • END{}
    • 1.所有文件读取完成之后,走END{}中的指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@m01 ~]# awk 'BEGIN{xxx}{print $1}END{print 1/3}'  roger.txt

[root@m01 ~]# awk -F: 'BEGIN{print "name","uid"}{print $1,$3}END{print "文件处理完成"}' /etc/passwd|column -t

[root@m01 ~]# awk -F: 'BEGIN{print "name","uid","gid"}{print $1,$3,$4}END{print "sb"}' /etc/passwd|column -t
name uid gid
root 0 0
bin 1 1
daemon 2 2
adm 3 4
lp 4 7
sync 5 0
shutdown 6 0
halt 7 0
mail 8 12
operator 11 0
games 12 100
ftp 14 50

awk的行与列

行:记录 record

列:字段 field

awk取行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
NR:Number of Record

[root@m01 ~]# awk 'NR==1' /etc/passwd
root:x:0:0:root:/root:/bin/bash

## 范围取行
[root@m01 ~]# awk 'NR>=1 && NR<=3' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

[root@m01 ~]# awk 'NR==1,NR==3' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

## 包含roger和cls的行
[root@m01 ~]# awk '/roger|cls/' roger.txt
1,roger,666
3,cls,888
roger,111

[root@m01 ~]# awk 'NR>=3' roger.txt
/tmp/check_2.txt
/tmp/check_3.txt
/tmp/check_4.txt
/tmp/check_5.txt
3,cls,888
roger,111
4,lls,999

[root@m01 ~]# awk '/roger/,/cls/' roger.txt
1,roger,666
/tmp/check_1.txt
/tmp/check_2.txt
/tmp/check_3.txt
cls
roger
4,lls,999
cls

## awk的控制结束标记
Record Separator
[root@m01 ~]# awk -vRS=, 'NR==1' roger.txt

## 修改输出内容的分割符
awk -F : -vOFS='|' '{print $1,$2,$NF}' /etc/passwd
awk -F : -vOFS='# '{print $1,$2,$NF}' /etc/passwd

awk取列

1
2
3
4
5
6
7
8
9
10
11
12
FS:内置变量,列分隔符  -F: = -vFS=:

[root@m01 ~]# awk -vFS=: '{print $1}' /etc/passwd

[root@m01 ~]# awk -vFS=: '{print $1,$NF}' /etc/passwd

[root@m01 ~]# awk -F: '{print $1"#"$2"#"$3"#"$4"#"$5"|"$6","$NF}' /etc/passwd

## 修改输出后内容的分隔符
[root@m01 ~]# awk -F: -vOFS=# '{print $1,$2,$3,$4,$5,$6,$NF}' /etc/passwd

[root@m01 ~]# awk -F: '{print $0}' /etc/passwd

awk取行取列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
## 取出top中的运行时间
[root@m01 ~]# top -n1 |awk 'NR==1{print $5}'
1

## 取出网卡配置文件的IP地址
[root@m01 ~]# awk -F= '/IPADDR/{print $2}' /etc/sysconfig/network-scripts/ifcfg-eth0
10.0.0.61

[root@m01 ~]# cat user.txt
1 Zeng Laoshi 133411023 :110:100:75
2 Deng Ziqi 44002231 :250:10:88
3 Zhang Xinyu 877623568 :120:300:200
4 Gu Linazha 11029987 :120:30:79
5 Di Lireba 253097001 :220:100:200
6 Jiang Shuying 535432779 :309:10:2
7 Ju Jingyi 68005178 :130:280:385
8 Zhang Yuqi 376788757 :500:290:33
9 Wen Zhang 259872003 :100:200:300


# 请找出姓氏是张的人,他们第二次捐款的数额及姓名
############# 有问题的写法 #############
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}/^Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}/Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290
Wen Zhang 200

############# 正确的写法 #############
[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}$2~/Zhang/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300
Zhang Yuqi 290

[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓","名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2,$3,$6}' user.txt
姓 名 捐款数额
Zhang Xinyu 300

[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2 $3,$6}' user.txt |column -t
姓名 捐款数额
ZhangXinyu 300

[root@m01 ~]# awk -F '[ :]+' 'BEGIN{print "姓名","捐款数额"}$2~/Zhang/ && $3~/X/{print $2 $3,$(NF-1)}' user.txt |column -t
姓名 捐款数额
ZhangXinyu 300

### 显示所有以25开头的QQ号及姓名
[root@m01 ~]# awk '$4~/^25/{print $2 $3,$4}' user.txt
DiLireba 253097001
WenZhang 259872003

### 显示所有QQ号最后一位是1或者3的人,全名及QQ
[root@m01 ~]# awk '$4~/1$|3$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/(1|3)$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003
[root@m01 ~]# awk '$4~/[13]$/{print $2$3,$4}' user.txt
ZengLaoshi 133411023
DengZiqi 44002231
DiLireba 253097001
WenZhang 259872003

### 显示每个捐款值都以$开头 $110:$00$75
[root@m01 ~]# awk '{gsub(/:/,"$");print $0}' user.txt
1 Zeng Laoshi 133411023 $110$100$75
2 Deng Ziqi 44002231 $250$10$88
3 Zhang Xinyu 877623568 $120$300$200
4 Gu Linazha 11029987 $120$30$79
5 Di Lireba 253097001 $220$100$200
6 Jiang Shuying 535432779 $309$10$2
7 Ju Jingyi 68005178 $130$280$385
8 Zhang Yuqi 376788757 $500$290$33
9 Wen Zhang 259872003 $100$200$300

[root@m01 ~]# awk '{gsub(/:/,"$",$5);print $0}' user.txt
1 Zeng Laoshi 133411023 $110 :100:75
2 Deng Ziqi 44002231 $250 :10:88
3 Zhang Xinyu 877623568 $120 :300:200
4 Gu Linazha 11029987 $120 :30:79
5 Di Lireba 253097001 $220 :100:200
6 Jiang Shuying 535432779 $309 :10:2
7 Ju Jingyi 68005178 $130 :280:385
8 Zhang Yuqi 376788757 $500 :290:33
9 Wen Zhang 259872003 $100 :200:300

function gsub(){
$1
$2
$3
xxxx
}

gsub(xx,aaa,d)
gsub("被替换的内容","替换的新内容")
gsub("被替换的内容","替换的新内容",第N列)

## 综合应用:找出ifconfig中范围是1-255的数字
[root@m01 ~]# ifconfig |awk -vRS='[^0-9]+' '$0>=1 && $0<=255'
10
61
255
255
255
10
255
6
80
20
29
3
64
20
29
3
2
5
2
6
1
172
16
1
61
255
255
255
172
16
1
255
6
80
20
29
3
9
64
20
29
3
9
46
9
117
29
7
73
127
1
255
6
1
128
10

awk模式与动作

1
awk -F: 'NR==1{print $1,$3}' /etc/passwd

上面这条命令我们可以看到,'NR==1{print $1,$3}'
可以理解为,'模式{动作}' == '条件{指令}'

awk中的模式

  • 正则表达式
1
2
3
4
5
6
# 正则表达式写法
'/正则表达式/flag'
'$1~/正则表达式/flag'
'$1!~/正则表达式/flag'

只不过我们在awk中很少使用flag
  • 比较表达式
1
2
3
4
5
NR==1
NR>=10
NR<=100
NR>=1 && NR<=10
$1>=100
  • 范围模式
1
2
3
4
5
6
7
8
9
## 精确匹配行号:从第10行到第20行
NR==10,NR==20

## 精确匹配字符串:从该字符串的行到另一个字符串所在行
'/root/,/roger/'
'/从哪个字符串所在行/,/到那个字符串所在行/' #中间的行都包含进去

## 模糊匹配字符串:从含有该字符串所在行到含有另一字符串所在行
'$1~/oo/,$1~/zl/'
  • 特殊模式
1
2
BEGIN
END

awk中的动作

在awk中,我们最常用的动作就是 print

当然我们还有别的动作可以使用:

  • print打印
  • gsub替换
  • 变量赋值
  • 统计计算
1
2
useradd name;pass=`echo $RANDOM|md5sum|cut -c 1-10`;echo $pass|passwd --stdin name;echo $pass:$user >> /tmp/user.txt
seq 100|awk '{print "useradd test"$1";pass=`echo $RANDOM|md5sum|cut -c 1-10`;echo $pass|passwd --stdin test"$1";echo $pass:test"$1" >> /tmp/user.txt"}'|bash

如果要使用BEGIN模式,那么一定要成双成对的出现:BEGIN{}

那么我们要知道,BEGIN{}中,大括号里面的内容,会在读取文件内容之前执行

主要应用场景:

  • 1.计算
1
2
[root@zabbix01 ~]# awk 'BEGIN{print 1/3}'
0.333333
  • 2.awk功能测试
  • 3.输出表格的表头

END模式

一般来说,END{}要比BEGIN{}重要一些,BEGIN{}可有可无,计算其实可以放在读取文件的时候,也可以执行

END{}中,大括号里面的内容,会在awk读取完文件的最后一行后,进行处理

作用:一般我们使用END{}来显示对日志内容分析后的一个结果
当然,还有其他功能,比如文件读取完了,可以显示一些尾部信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 1.统计/etc/service文件中一共有多少行
[root@m01 ~]# awk '{hang++;print hang}' /etc/services
1
...
11176

不需要过程,只要结果

[root@m01 ~]# awk '{hang++}END{print hang}' /etc/services
11176

### 只能统计文件的所有行数
[root@m01 ~]# awk 'END{print NR}' /etc/services
11176

### 流氓写法
[root@m01 ~]# sed -n '$=' /etc/services
11176
[root@m01 ~]# grep -c '.*' /etc/services
11176
[root@m01 ~]# wc -l /etc/services
11176 /etc/services

# 2.统计/etc/service中空行的数量
[root@m01 ~]# awk '/^$/{print}' /etc/services

[root@m01 ~]# awk '/^$/{i++}END{print i}' /etc/services
17

# 3.统计出下列文件中所有人的年龄和
### 脚本方式
#!/usr/bin/bash
n=0
for line in `cat user.txt`;do
if [[ $line =~ [0-9]+ ]];then
((n+=$line))
fi
done
echo $n

### awk方式
[root@m01 ~]# cat user.txt
姓名 年龄
曾老湿 23
苍颈空 18
西冶详 99
[root@m01 ~]# awk 'NR>1{print $2}' user.txt
23
18
99
[root@m01 ~]# awk 'NR>1{n+=$2}END{print n}' user.txt
140

# 4.统计nginx日志中,状态码是200的次数以及,状态码是200时占用的流量
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz |awk 'BEGIN{print "状态码200的次数","总流量"}$10~/200/{code++;byte+=$11}END{print code,byte}'|column -t
状态码200的次数 总流量
3100 190477111

脚本写法:
awk '
BEGIN{
print "状态码200的次数","总流量"
}
$10~/200/{
code++;byte+=$11
}
END{
print code,byte
}'

# 5.统计nginx日志中状态码是4xx和5xx的次数及总流量
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz|awk '$10~/^[45]/{i++;n+=$11}END{print i,n}'
580 519243

# 6.综合应用:分别统计每种状态码的次数和每个状态码的总流量
zcat blog.driverzeng.com_access.log-20220623.gz |awk '
BEGIN{
print "状态码","总流量"
}
$10~/200/{
i1++;n1+=$11
}
$10~/^3/{
i2++;n2+=$11
}
$10~/^4/{
i3++;n3+=$11
}
$10~/^5/{
i4++;n4+=$11
}
END{
print "200次数:"i1,"200的流量:"n1
print "3xx次数:"i2,"3xx的流量:"n2
print "4xx次数:"i3,"4xx的流量:"n3
print "5xx次数:"i4,"5xx的流量:"n4
}'|column -t

awk数组

在awk中的数组数据类型,是非常好用的一个类型,不像是shell,当然shell中数组也有它自己的优点。

awk中的数组,专门用来统计不同的分类。

** 例如:**
1.nginx日志中每个IP出现的次数
2.nginx日志中每种状态码出现的次数
3.nginx日志中每个URI的访问次数

1
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz|awk '{print $1}'|sort|uniq -c|sort -nr

awk数组赋值

1
[root@m01 ~]# awk 'BEGIN{array[0]="roger";array[1]="wyk"}'

awk数组取值

1
2
[root@m01 ~]# awk 'BEGIN{array[0]="roger";array[1]="wyk";print array[0],array[1]}'
roger wyk

shell中循环数组

1
2
3
4
5
6
array[0]='roger'
array[1]='cls'

for name in ${array[*]};do
echo $name
done

awk循环数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
for(条件){
动作
}

for 条件;do
动作
done

[root@m01 ~]# awk 'BEGIN{array[0]="roger";array[1]="wyk";for(num in array){print num}}'
0
1
[root@m01 ~]# awk 'BEGIN{array[0]="roger";array[1]="wyk";for(num in array){print array[num]}}'
roger
wyk

## 统计nginx日志中的每一个IP地址访问的次数
zcat blog.driverzeng.com_access.log-20220623.gz |awk '{array[$1]++}END{for(ip in array){print ip,array[ip]}}'

#1.取出下列域名并根据域名,进行统计排序处理
https://blog.driverzeng.com/index.html
https://blog.driverzeng.com/1.html
http://post.driverzeng.com/index.html
http://mp3.driverzeng.com/index.html
https://blog.driverzeng.com/3.html
http://post.driverzeng.com/2.html

[root@m01 ~]# awk -F/ '{domain[$3]++}END{for(name in domain){print name,domain[name]}}' 1.txt
blog.driverzeng.com 3
post.driverzeng.com 2
mp3.driverzeng.com 1

#2.统计nginx日志中,每个IP访问使用的流量总和
[root@m01 ~]# zcat blog.driverzeng.com_access.log-20220623.gz |awk '{ip[$1]++;liuliang[$1]+=$11}END{for(i in ip){print i,ip[i],liuliang[i]}}'

awk的判断

awk判断与shell判断对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
## shell
if [ 条件 ];then
动作
fi

if [ 条件 ];then
else
fi

if [ 条件 ];then
elif [ 条件 ];then
else
fi

## awk
if(条件){
动作
}

if(条件){
动作
}else{
动作
}

if(条件){
动作
}else if(条件){
动作
}else{
动作
}

awk '{}END{for(条件){if(条件){动作}else if(条件){动作}else{动作}}}'
awk '{
读文件的动作
}END{
for(条件){
if(条件){
动作
}else if(条件){
动作
}else{
动作
}
}
}'

#1.判断磁盘使用率大于70%,大于显示磁盘空间不足,不大于显示正常
## 取出磁盘使用率

[root@m01 ~]# df -h|awk -F '[ %]+' 'NR==2{if($5>70){print "磁盘空间不足"}else{print "磁盘空间还行"}}'
磁盘空间还行
[root@m01 ~]# df -h|awk -F '[ %]+' 'NR==2{if($5>70){print "磁盘空间不足"}else{print "磁盘空间还行,当前磁盘使用率:"$5"%"}}'
磁盘空间还行,当前磁盘使用率:9%
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
### 作业:
#1.从1加到100

#2.企业面试题:统计每个学生的总成绩和平均成绩
stu01 70 80 90 100 90 80
stu02 89 78 98 67 97 90
stu03 56 12 33 44 55 66 77
stu04 89 78 77 99 100 30
stu05 98 97 96 95 94 93
stu06 100 20 20 20 20 20

#3.从下面文件内容中取值
Zeng Laoshi 133411023 :110:100:75
Deng Ziqi 44002231 :250:10:88
Zhang Xinyu 877623568 :120:300:200
Gu Linazha 11029987 :120:30:79
Di Lireba 253097001 :220:100:200
Jiang Shuying 535432779 :309:10:2
Ju Jingyi 68005178 :130:280:385
Zhang Yuqi 376788757 :500:290:33
Wen Zhang 259872003 :100:200:300


1.请找出姓氏是张的人,他们第二次捐款的数额及姓名
2.显示所有以25开头的QQ号及姓名
3.显示所有QQ号最后一位是1或者3的人,全名及QQ
4.显示每个捐款值都以$开头 $110:$00$75
5.综合应用:找出ifconfig中范围是1-255的数字