Shell脚本(二)


shell脚本可以让降低大家的工作强度,提升大家的管理能力和薪资报酬,还可以让大家有时间学习提升自己,更能让你有时间喝茶撩妹子。所以,学好shell那就是非常必要的啦,那么在学习shell之前我们得热热身,做一下准备工作了。

学习之前首先要弄清什么是shell脚本:

    shell脚本本质上就是要将完成一件事情的所有命令按照执行的先后顺序写入一个文件,并给予文件执行权限

so!想学好shell脚本首先要考核一下自己的linux命令学的是否扎实,接下来为了能让大家学习shell更加顺畅,我把常用的一些数据处理的命令给大家回顾一下。

我们重点回顾以下几类命令

数据检索命令

         行检索:grep  egrep

         字符串检索:cut  tr

数据处理命令       

    数据排序:sort

    数据去重: uniq

    文本数据合并: paste

    数据输出: tee

    数据处理: xargs    

一、 数据处理命令

1、grep: 负责从数据源中检索对应的字符串,行过滤。

grep用于根据关键字进行行过滤
grep options 'keys' filename
OPTIONS:
    -i: 不区分大小写
    -v: 查找不包含指定内容的行,反向选择
    -w: 按单词搜索
    -n: 显示行号
    -A: 显示匹配行及后面多少行 -A 5
    -B: 显示匹配行及前面多少行

    -o: 打印匹配关键字
    -c: 统计匹配到的次数
    -r: 逐层遍历目录查找
    -C: 显示匹配行前后多少行
    -l:只列出匹配的文件名
    -L:列出不匹配的文件名
    -e: 使用正则匹配
    -E:使用扩展正则匹配
    ^key:以关键字开头
    key$:以关键字结尾
    ^$:匹配空行
    --color=auto :可以将找到的关键词部分加上颜色的显示


常用命令选项必知必会  示例:
# grep -i root passwd 忽略大小写匹配包含root的行
# grep -w ftp passwd 精确匹配ftp单词
# grep -wo ftp passwd 打印匹配到的关键字ftp
# grep -n root passwd 打印匹配到root关键字的行好
# grep -ni root passwd 忽略大小写匹配统计包含关键字root的行
# grep -nic root passwd 忽略大小写匹配统计包含关键字root的行数
# grep -i ^root passwd 忽略大小写匹配以root开头的行
# grep bash$ passwd 匹配以bash结尾的行
# grep -n ^$ passwd 匹配空行并打印行号
# grep ^# /etc/vsftpd/vsftpd.conf 匹配以#号开头的行
# grep -v ^# /etc/vsftpd/vsftpd.conf 匹配不以#号开头的行
# grep -A 5 mail passwd   匹配包含mail关键字及其后5行
# grep -B 5 mail passwd   匹配包含mail关键字及其前5行
# grep -C 5 mail passwd 匹配包含mail关键字及其前后5行



centos8中已经为大家设置了,存放在/etc/profile.d/colorgrep.sh文件中,如若大家使用的系统中没有设置颜色输出,
可以使用以下方法来自行设置

临时设置:
# alias grep='grep --color=auto' //只针对当前终端和当前用户生效

永久设置:
1)全局(针对所有用户生效)
vim /etc/bashrc
alias grep='grep --color=auto'
source /etc/bashrc

2)局部(针对具体的某个用户)
vim ~/.bashrc
alias grep='grep --color=auto'

注意:如果希望你对环境变量的设置立刻生效,可以使用以下命令而不需要重启计算机
source ~/.bashrc

2、cut数据截取

cut用于列截取
-c: 以字符为单位进行分割。
-d: 自定义分隔符,默认为制表符。\t
-f: 与-d一起使用,指定显示哪个区域。

# cut -d: -f1 1.txt 以:冒号分割,截取第1列内容
# cut -d: -f1,6,7 1.txt 以:冒号分割,截取第1,6,7列内容
# cut -c4 1.txt 截取文件中每行第4个字符
# cut -c1-4 1.txt 截取文件中每行的1-4个字符
# cut -c4-10 1.txt 
# cut -c5- 1.txt 从第5个字符开始截取后面所有字符

3、tr 字符转换:替换,删除

tr用来从标准输入中通过替换或删除操作进行字符转换;主要用于删除文件中控制字符或进行字符转换。
使用tr时要转换两个字符串:字符串1用于查询,字符串2用于处理各种转换。

语法:
commands|tr  'string1'  'string2'
tr  'string1'  'string2' < filename

tr options 'string1' < filename

-d 删除字符串1中所有输入字符。
-s 删除所有重复出现字符序列,只保留第一个;即将重复出现字符串压缩为一个字符串。


a-z 任意小写
A-Z 任意大写
0-9 任意数字

[root@zuolaoshi shell01]# cat 3.txt 自己创建该文件用于测试
ROOT:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
boss02:x:516:511::/home/boss02:/bin/bash
vip:x:517:517::/home/vip:/bin/bash
stu1:x:518:518::/home/stu1:/bin/bash
mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin
smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin
aaaaaaaaaaaaaaaaaaaa
bbbbbb111111122222222222233333333cccccccc
hello world 888
666
777
999


# tr -d '[:/]' < 3.txt 删除文件中的:和/
# cat 3.txt |tr -d '[:/]' 删除文件中的:和/
# tr '[0-9]' '@' < 3.txt 将文件中的数字替换为@符号
# tr '[a-z]' '[A-Z]' < 3.txt 将文件中的小写字母替换成大写字母
# tr -s '[a-z]' < 3.txt 匹配小写字母并将重复的压缩为一个
# tr -s '[a-z0-9]' < 3.txt 匹配小写字母和数字并将重复的压缩为一个

4、sort排序

sort:将文件的每一行作为一个单位,从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。

语法:
sort [options] [filename]

-u :去除重复行
-r :降序排列,默认是升序
-o : 将排序结果输出到文件中  类似 重定向符号 >
-n :以数字排序,默认是按字符排序
-t :分隔符
-k :第N列

-b :忽略前导空格。
-R :随机排序,每次运行的结果均不同。

 示例:
# sort -n -t: -k3 1.txt 按照用户的uid进行升序排列
# sort -nr -t: -k3 1.txt 按照用户的uid进行降序排列
# sort -n 2.txt 按照数字排序
# sort -nu 2.txt 按照数字排序并且去重
# sort -nr 2.txt 
# sort -nru 2.txt 
# sort -nru 2.txt 
# sort -n 2.txt -o 3.txt 按照数字排序并将结果重定向到文件
# sort -R 2.txt 
# sort -u 2.txt 

5、uniq 去除连续的重复行

应用技巧:去重前先使用sort排序

uniq:去除连续重复行

语法:
uniq [options] [filename]

-i: 忽略大小写
-c: 统计重复行次数
-d:只显示重复行

# uniq 2.txt 
# uniq -d 2.txt 
# uniq -dc 2.txt 

6、tee 双向输出

tee工具从标准输入读取并写入标准输出和文件,即:双向覆盖重定向<屏幕输出|文本输入>
somecommand |tee filename

-a 双向追加重定向

# echo hello world
# echo hello world|tee file1
# cat file1 
# echo 999|tee -a file1
# cat file1 

7、paste

paste工具用于合并文件行输出到屏幕,不会改动源文件

-d:自定义间隔符,默认是tab,只接受一个字符
-s:将每个文件中的所有内容按照一行输出,文件中的行与行以TAB间隔。

[root@zuolaoshi shell01]# cat a.txt 
hello
[root@zuolaoshi  shell01]# cat b.txt 
hello world
888
999
[root@zuolaoshi  shell01]# paste a.txt b.txt 
hello   hello world
        888
        999
[root@zuolaoshi  shell01]# paste b.txt a.txt   
hello world     hello
888
999

[root@zuolaoshi shell01]# paste -d'@' b.txt a.txt 
hello world@hello
888@
999@

[root@zuolaoshi shell01]# paste -s b.txt a.txt 
hello world     888     999
hello

8、xargs 上一个命令的输出作为下一个命令的命令行参数

管道(|):上一个命令的输出作为下一个命令的输入,做的是数据源

[root@manage01 ~]# sort -n 2.txt |uniq 
1
2
3
5
6
10
99
9999

如何将上一个命令的输出,作为下一个命令的参数呢?

xargs 上一个命令的输出作为下一个命令的命令行参数

回顾:
linux 命令格式
命令   命令选项   参数
ls -l /
========================
xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据。

xargs 一般是和管道一起使用。

命令格式:
''[somecommand]|[filename]'' |xargs -item  command

OPTIONS:
-a file 从文件中读入作为sdtin
-E flag flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。
-p 当每次执行一个argument的时候询问一次用户。
-n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。
-t 表示先打印命令,然后再执行。
-i 或者是-I,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给 {},可以用 {} 代替。
-r no-run-if-empty 当xargs的输入为空的时候则停止xargs,不用再去执行了。
-d delim 分隔符,默认的xargs分隔符是回车,argument的分隔符是空格,这里修改的是xargs的分隔符。


注意:linux命令格式一般为
命令    命令选项     参数
上一个命令的输出就是下一个命令的参数  这句话结合命令语法  应该知道输出的内容在下一个命令的位置了吧。


案例
[root@zuolaoshi  ~]# find / -name zuolaoshi |xargs gzip 
[root@zuolaoshi  ~]# cat 1
1
2
3
4
5
6
7
8
9
10
[root@zuolaoshi  ~]# xargs -a 1 
1 2 3 4 5 6 7 8 9 10

[root@zuolaoshi  ~]# xargs -a 1 -E 5 
1 2 3 4

这样就明白使用xargs -a 为什么读取文件的时候会把文件中的所有内容都输出了吧
[root@zuolaoshi  ~]# xargs -a 1 -p
echo 1 2 3 4 5 6 7 8 9 10 ?...y
1 2 3 4 5 6 7 8 9 10
[root@zuolaoshi  ~]# xargs -a 1 -p
echo 1 2 3 4 5 6 7 8 9 10 ?...n

同理为什么把文件中的所有行按一行输出呢,原因就是默认输出所有
[root@zuolaoshi  ~]# xargs -a 1 -n3
1 2 3
4 5 6
7 8 9
10
[root@zuolaoshi  ~]# xargs -a 1 -n3 -p
echo 1 2 3 ?...y
echo 4 5 6 ?...1 2 3
y
echo 7 8 9 ?...4 5 6
y
echo 10 ?...7 8 9
y
10

和-p命令选项一样,显示他是怎么执行的,只不过这个不需要确认。
[root@zuolaoshi  ~]# cat 1 |xargs -t
echo 1 2 3 4 5 6 7 8 9 10 
1 2 3 4 5 6 7 8 9 10

为何读入文件会把所有行都放在一行呢?这个和xargs的列分割符有关系
默认是回车
我们可以使用-d 改掉默认列与列的默认分割符为其他,自然就会换行了
[root@zuolaoshi  ~]# xargs -a 1 -d "@"
1
2
3
4
5
6
7
8
9
10

9、shell字符

有基础的同学不要和正则表达式中的符号含义搞混淆了。    
    !:                执行历史命令   !! 执行上一条命令
    $:                变量中取内容符
    + - * / %:        对应数学运算      取余数  
    &:                后台执行
    ;               分号可以在shell中一行执行多个命令,命令之间用分号分割    
    \:                转义字符
    ``:               反引号 命令中执行命令    echo "today is `date +%F`"
    ' ':              单引号,脚本中字符串要用单引号引起来,但是不同于双引号的是,单引号不解释变量
    " ":              双引号,脚本中出现的字符串可以用双引号引起来

通配符    
    ~:                家目录    # cd ~ 代表进入用户家目录
    *:                星号是shell中的通配符  匹配所有
    ?:                问号是shell中的通配符  匹配除回车以外的一个字符
    [list]: 匹配[list]中的任意单个字符
[!list]: 匹配除list中的任意单个字符
{string1,string2,...} 匹配string1,string2或更多字符串


重定向
>      覆盖输入 
>> 追加输入
< 输出
<< 追加输出

管道命令
|               管道符 上一个命令的输出作为下一个命令的输入   cat filename | grep "abc"

10、组合命令实战

job1: 检索本机的IP、NETMASK、MAC地址、广播地址
IP:  172.20.10.3
NetMask:  255.255.255.240
Broadcast:  172.20.10.15
MAC Address:  00:0c:29:8d:49:ea


job2: 将系统中所有普通用户的用户名、密码和默认shell保存到一个文件中,要求用户名密码和默认shell之间用tab键分割
ayitula x /bin/bash

组合命令实战代码

job1: 检索本机的IP、NETMASK、MAC地址、广播地址
[root@zuolaoshi  ~]# ifconfig ens33   检索网卡信息
[root@zuolaoshi  ~]# ifconfig ens33|grep -w inet|tr -d '[a-zA-Z]'|tr -s " "  处理检索行

IP地址
[root@zuolaoshi  ~]# ifconfig ens33|grep -w inet|tr -d [a-zA-Z]|tr -s " "|cut -d " " -f2|xargs echo "IP: "
IP:  172.20.10.3

NetMask地址
[root@zuolaoshi  ~]# ifconfig ens33|grep -w inet|tr -d [a-zA-Z]|tr -s " "|cut -d " " -f3|xargs echo "NetMask: "
NetMask:  255.255.255.240

广播地址
[root@zuolaoshi  ~]# ifconfig ens33|grep -w inet|tr -d [a-zA-Z]|tr -s " "|cut -d " " -f4|xargs echo "Broadcast: "
Broadcast:  172.20.10.15

MAC地址
[root@zuolaoshi  ~]# ifconfig ens33|grep -w ether|tr -s " "|cut -d " " -f3|xargs echo "MAC Address: "
MAC Address:  00:0c:29:8d:49:ea

job2: 将系统中所有普通用户的用户名、密码和默认shell保存到一个文件中,要求用户名密码和默认shell之间用tab键分割
[root@zuolaoshi  ~]# cut -d ":" -f1,2,7 /etc/passwd|tr ":" "\t"|grep -i "bash"|grep -v "root"
ayitula x /bin/bash

聊聊大家常说的数据分析:

  • 数据收集:负责数据的收集
  • 数据清洗:负责数据的筛选
  • 数据分析:数据运算、整理
  • 数据展示:图表或表格方式输出结果

shell脚本数据的处理

1)数据检索:grep tr cut

2)数据处理:uniq sort tee paste xargs

之前的脚本中我们都是通过grep、cut、tr、uniq、sort等命令通过管道组合在一起将字符串检索出来,然后在通过shell中对应的运算得到结果,在数据检索过程中大家可能也体会到了其中的辛苦和蹩脚。没办法,会的就这么多,还需要完成任务。

缺点:复杂的命令组合

多次运算

上手难

解决办法

好了,学完这节课大家的所有之前的痛苦就都能解决了,今天要给大家介绍一个更加厉害的命令awk。他可以让大家从输出流中检索出自己需要的数据而不需要再向以前那样通过大量命令组合来完成,只需一个命令awk就能完成。并且还能够通过awk对数据进行处理,而不再需要额外的shell运算。

awk的应用场景

字符串截取

数据运算

比如内存使用率脚本

在shell脚本编写中,时常会用到对文件的相关操作,比如增加内容,修改内容,删除部分内容,查看部分内容等,但是上述举例的这些操作一般都是需要在文本编辑器中才能操作,常用的文本编辑器如:gedit、vim、nano等又是交互式文本编辑器,脚本无法自己独立完成,必须有人参与才可以完成。如果这样的话又违背了我们编写脚本的意愿(全部由机器来完成,减少人的工作压力,提升工作效率)。emm…如何才能让这些操作全部脚本自己就搞定,而不需要人的参与,而且又能按照我们的脚本预案来完成呢?

为了解决上述问题,linux为大家提供了一些命令,比如Perl、sed等命令,今天我就着重为大家介绍一下sed命令。

一、sed介绍

sed是linux中提供的一个外部命令,它是一个行(流)编辑器,非交互式的对文件内容进行增删改查的操作,使用者只能在命令行输入编辑命令、指定文件名,然后在屏幕上查看输出。它和文本编辑器有本质的区别。

区别是:

文本编辑器: 编辑对象是文件

行编辑器:编辑对象是文件中的行

也就是前者一次处理一个文本,而后者是一次处理一个文本中的一行。这个是我们应该弄清楚且必须牢记的,否者可能无法理解sed的运行原理和使用精髓。

sed数据处理原理

sed处理数据原理.png

二、sed语法

sed 命令语法:

sed [options] ‘{command}[flags]’ [filename]

#命令选项
-e script 将脚本中指定的命令添加到处理输入时执行的命令中  多条件,一行中要有多个操作
-f script 将文件中指定的命令添加到处理输入时执行的命令中
-n        抑制自动输出
-i        编辑文件内容
-i.bak    修改时同时创建.bak备份文件。
-r        使用扩展的正则表达式
!         取反 (跟在模式条件后与shell有所区别)

#command   对文件干什么
sed常用内部命令
a   在匹配后面添加
i   在匹配前面添加

d   删除

s   查找替换  字符串
c   更改
y   转换   N D P 


p   打印



#flags
数字             表示新文本替换的模式
g:             表示用新文本替换现有文本的全部实例
p:             表示打印原始的内容
w filename:     将替换的结果写入文件

2.1)sed内部命令说明

演示实例文档

[root@zuolaoshi ~]# cat data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容增加操作,将数据追加到某个位置之后,使用命令a。

演示案例

在data1的每行后追加一行新数据内容: append data "haha"
[root@zuolaoshi ~]# sed 'a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
append data "haha"
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.
append data "haha"


在第二行后新开一行追加数据: append data "haha"
[root@zuolaoshi ~]# sed '2a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

在第二到四行每行后新开一行追加数据: append data "haha"
[root@zuolaoshi ~]# sed '2,4a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
append data "haha"
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
append data "haha"
5 the quick brown fox jumps over the lazy dog.

匹配字符串追加: 找到包含"3 the"的行,在其后新开一行追加内容: append data "haha"
[root@zuolaoshi ~]# sed '/3 the/a\append data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
append data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

//开启匹配模式  /要匹配的字符串/

文件内容增加操作,将数据插入到某个位置之前,使用命令i。

演示案例

在data1的每行前插入一行新数据内容: insert data "haha"
[root@zuolaoshi ~]# sed 'i\insert data "haha"' data1
insert data "haha"
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
insert data "haha"
5 the quick brown fox jumps over the lazy dog.


在第二行前新开一行插入数据: insert data "haha"
[root@zuolaoshi ~]# sed '2i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.


在第二到四行每行前新开一行插入数据: insert data "haha"
[root@zuolaoshi ~]# sed '2,4i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
insert data "haha"
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
insert data "haha"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.


匹配字符串插入: 找到包含"3 the"的行,在其前新开一行插入内容: insert data "haha"
[root@zuolaoshi ~]# sed '/3 the/i\insert data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
insert data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作–替换,将一行中匹配的内容替换为新的数据,使用命令s。

演示案例

从标准输出流中做替换,将test替换为text
[root@zuolaoshi ~]# echo "this is a test" |sed 's/test/text/'
this is a text

将data1中每行的dog替换为cat
[root@zuolaoshi ~]# sed 's/dog/cat/' data1
1 the quick brown fox jumps over the lazy cat.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy cat.
5 the quick brown fox jumps over the lazy cat.

将data1中第二行的dog替换为cat
[root@zuolaoshi ~]# sed '2s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

将data1中第二到第四行的dog替换为cat
[root@zuolaoshi ~]# sed '2,4s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy cat.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy cat.
5 the quick brown fox jumps over the lazy dog.

匹配字符串替换:将包含字符串"3 the"的行中的dog替换为cat
[root@zuolaoshi ~]# sed '/3 the/s/dog/cat/' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy cat.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作–更改,将一行中匹配的内容替换为新的数据,使用命令c。

演示案例

将data1文件中的所有行的内容更改为: change data "data"
[root@zuolaoshi ~]# sed 'c\change data "haha"' data1
change data "haha"
change data "haha"
change data "haha"
change data "haha"
change data "haha"

将data1文件第二行的内容更改为: change data "haha"
[root@zuolaoshi ~]# sed '2c\change data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
change data "haha"
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

将data1文件中的第二、三、四行的内容更改为:change data "haha"
[root@zuolaoshi ~]# sed '2,4c\change data "haha"' data1
1 the quick brown fox jumps over the lazy dog.
change data "haha"
5 the quick brown fox jumps over the lazy dog.

将data1文件中包含"3 the"的行内容更改为: change data "haha"
[root@zuolaoshi ~]# sed '/3 the/c\change data "data"' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
change data "data"
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容修改操作–字符转换,将一行中匹配的内容替换为新的数据,使用命令y。

演示案例

将data1中的a b c字符转换为对应的 A  B  C字符
[root@zuolaoshi ~]# sed 'y/abc/ABC/' data1
1 the quiCk Brown fox jumps over the lAzy dog.
2 the quiCk Brown fox jumps over the lAzy dog.
3 the quiCk Brown fox jumps over the lAzy dog.
4 the quiCk Brown fox jumps over the lAzy dog.
5 the quiCk Brown fox jumps over the lAzy dog.

文件内容删除,将文件中的指定数据删除,使用命令d。

演示案例

删除文件data1中的所有数据
[root@zuolaoshi ~]# sed 'd' data1

删除文件data1中的第三行数据
[root@zuolaoshi ~]# sed '3d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

删除文件data1第三到第四行的数据
[root@zuolaoshi ~]# sed '3,4d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

删除文件data1中包含字符串"3 the"的行
[root@zuolaoshi ~]# sed '/3 the/d' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

文件内容查看,将文件内容输出到屏幕,使用命令p。

演示案例

打印data1文件内容
[root@zuolaoshi ~]# sed 'p' data1
1 the quick brown fox jumps over the lazy dog.
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

打印data1文件第三行的内容
[root@zuolaoshi ~]# sed '3p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

打印data1文件第二、三、四行内容
[root@zuolaoshi ~]# sed '2,4p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

打印data1文件包含字符串"3 the"的行
[root@zuolaoshi ~]# sed '/3 the/p' data1
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog. 


可以看得出,打印内容是重复的行,原因是打印了指定文件内容一次,又将读入缓存的所有数据打印了一次,所以会看到这样的效果,
如果不想看到这样的结果,可以加命令选项-n抑制内存输出即可。

2.2)命令选项说明

sed语法 在sed命令中,命令选项是对sed中的命令的增强

在命令行中使用多个命令 -e

将brown替换为green dog替换为cat
[root@zuolaoshi ~]# sed -e 's/brown/green/;s/dog/cat/' data1
1 the quick green fox jumps over the lazy cat.
2 the quick green fox jumps over the lazy cat.
3 the quick green fox jumps over the lazy cat.
4 the quick green fox jumps over the lazy cat.
5 the quick green fox jumps over the lazy cat.

从文件读取编辑器命令 -f 适用于日常重复执行的场景

1)将命令写入文件
[root@zuolaoshi ~]# vim abc
s/brown/green/
s/dog/cat/
s/fox/elephant/

2)使用-f命令选项调用命令文件
[root@zuolaoshi ~]# sed -f abc data1 
1 the quick green elephant jumps over the lazy cat.
2 the quick green elephant jumps over the lazy cat.
3 the quick green elephant jumps over the lazy cat.
4 the quick green elephant jumps over the lazy cat.
5 the quick green elephant jumps over the lazy cat.

抑制内存输出 -n

打印data1文件的第二行到最后一行内容  $最后的意思
[root@zuolaoshi ~]# sed -n '2,$p' data1 
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

使用正则表达式 -r

打印data1中以字符串"3 the"开头的行内容
[root@zuolaoshi ~]# sed -n  -r '/^(3 the)/p' data1
3 the quick brown fox jumps over the lazy dog.

从上述的演示中,大家可以看出,数据处理只是在缓存中完成的,并没有实际修改文件内容,如果需要修改文件内容可以直接使用-i命令选项。在这里我需要说明的是-i是一个不可逆的操作,一旦修改,如果想复原就很困难,几乎不可能,所以建议大家在操作的时候可以备份一下源文件。-i命令选项提供了备份功能,比如参数使用-i.bak,那么在修改源文件的同时会先备份一个以.bak结尾的源文件,然后再进行修改操作。

1)查看文件列表,没有发现data1.bak
[root@zuolaoshi ~]# ls
abc  apache  data1  Dobby  file  node-v10.14.1  Python-3.7.1  soft1  vimset

2)执行替换命令并修改文件
[root@zuolaoshi ~]# sed -i.bak 's/brown/green/' data1

3)发现文件夹中多了一个data1.bak文件
[root@zuolaoshi ~]# ls
abc     data1      Dobby  node-v10.14.1  soft1
apache  data1.bak  file   Python-3.7.1   vimset

4)打印比较一下,发现data1已经被修改,data1.bak是源文件的备份。
[root@zuolaoshi ~]# cat data1
1 the quick green fox jumps over the lazy dog.
2 the quick green fox jumps over the lazy dog.
3 the quick green fox jumps over the lazy dog.
4 the quick green fox jumps over the lazy dog.
5 the quick green fox jumps over the lazy dog.
[root@zuolaoshi ~]# cat data1.bak 
1 the quick brown fox jumps over the lazy dog.
2 the quick brown fox jumps over the lazy dog.
3 the quick brown fox jumps over the lazy dog.
4 the quick brown fox jumps over the lazy dog.
5 the quick brown fox jumps over the lazy dog.

2.3)标志

在sed命令中,标志是对sed中的内部命令做补充说明

演示文档
[root@zuolaoshi ~]# cat data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy dog . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

数字标志:此标志是一个非零正数,默认情况下,执行替换的时候,如果一行中有多个符合的字符串,如果没有标志位定义,那么只会替换第一个字符串,其他的就被忽略掉了,为了能精确替换,可以使用数字位做定义。

替换一行中的第二处dog为cat
[root@zuolaoshi ~]# sed 's/dog/cat/2' data2
1 the quick brown fox jumps over the lazy dog . cat
2 the quick brown fox jumps over the lazy dog . cat
3 the quick brown fox jumps over the lazy dog . cat
4 the quick brown fox jumps over the lazy dog . cat
5 the quick brown fox jumps over the lazy dog . cat

g标志:将一行中的所有符合的字符串全部执行替换

将data1文件中的所有dog替换为cat
[root@zuolaoshi ~]# sed 's/dog/cat/g' data2
1 the quick brown fox jumps over the lazy cat . cat
2 the quick brown fox jumps over the lazy cat . cat
3 the quick brown fox jumps over the lazy cat . cat
4 the quick brown fox jumps over the lazy cat . cat
5 the quick brown fox jumps over the lazy cat . cat

p标志:打印文本内容,类似于-p命令选项

[root@zuolaoshi ~]# sed  '3s/dog/cat/p' data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

w filename标志:将修改的内容存入filename文件中

[root@zuolaoshi ~]# sed  '3s/dog/cat/w text' data2
1 the quick brown fox jumps over the lazy dog . dog
2 the quick brown fox jumps over the lazy dog . dog
3 the quick brown fox jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy dog . dog
5 the quick brown fox jumps over the lazy dog . dog

可以看出,将修改的第三行内容存在了text文件中
[root@zuolaoshi ~]# cat text 
3 the quick brown fox jumps over the lazy cat . dog

三、练习案例

3.1、写一个初始化系统的脚本 案例需求 1)自动修改主机名(如:ip是192.168.0.88,则主机名改为server88.zuolaoshi.cc)

a. 更改文件非交互式 sed

/etc/sysconfig/network

b.将本主机的IP截取出来赋值给一个变量ip;再然后将ip变量里以.分割的最后一位赋值给另一个变量ip1

2)自动配置可用的yum源

3)自动关闭防火墙和selinux

1关闭防火墙
2关闭selinux
3配置yum源
4ntp时间服务器 
5更新系统软件包

3.2、写一个搭建ftp服务的脚本,要求如下: 案例需求 1)不支持本地用户登录 local_enable=NO 2) 匿名用户可以上传 新建 删除 anon_upload_enable=YES anon_mkdir_write_enable=YES 3) 匿名用户限速500KBps anon_max_rate=500000

仅供参考:
#!/bin/bash
ipaddr=`ifconfig eth0|sed -n '2p'|sed -e 's/.*inet addr:\(.*\) Bcast.*/\1/g'`
iptail=`echo $ipaddr|cut -d'.' -f4`
ipremote=192.168.3.200
#修改主机名
hostname server$iptail
sed -i "/HOSTNAME/HOSTNAME=server$iptail" /etc/sysconfig/network
echo "$ipaddr server$iptail" >>/etc/hosts
#关闭防火墙和selinux
systemctl stop    firewalld
systemctl disable firewalld
setenforce 0 >/dev/null 2>&1
sed -i '/^SELINUX=/SELINUX=disabled' /etc/selinux/config
#配置yum源(一般是内网源)
#test network
ping -c 1 $ipremote > /dev/null 2>&1
if [ $? -ne 0 ];then
    echo "你的网络不通,请先检查你的网络"
    exit 1
else
    echo "网络ok."
fi
cat > /etc/yum.repos.d/server.repo << end
[server]
name=server
baseurl=ftp://$ipremote
enabled=1
gpgcheck=0
end

#安装软件
read -p "请输入需要安装的软件,多个用空格隔开:" soft
yum -y install $soft &>/dev/null

#备份配置文件
conf=/etc/vsftpd/vsftpd.conf
\cp $conf $conf.default
#根据需求修改配置文件
sed -ir '/^#|^$/d' $conf
sed -i '/local_enable/c\local_enable=NO' $conf
sed -i '$a anon_upload_enable=YES' $conf
sed -i '$a anon_mkdir_write_enable=YES' $conf
sed -i '$a anon_other_write_enable=YES' $conf
sed -i '$a anon_max_rate=512000' $conf
#启动服务
service vsftpd restart &>/dev/null && echo"vsftpd服务启动成功"

#测试验证
chmod 777 /var/ftp/pub
cp /etc/hosts /var/ftp/pub
#测试下载
cd /tmp
lftp $ipaddr <<end
cd pub
get hosts
exit
end

if [ -f /tmp/hosts ];then
    echo "匿名用户下载成功"
    rm -f /tmp/hosts
else
    echo "匿名用户下载失败"
fi
#测试上传、创建目录、删除目录等
cd /tmp
lftp $ipaddr << end
cd pub
mkdir test1
mkdir test2
put /etc/group
rmdir test2
exit
end

if [ -d /var/ftp/pub/test1 ];then
    echo "创建目录成功"
    if [ ! -d /var/ftp/pub/test2 ];then
        echo "文件删除成功"
        fi
else
    if [ -f /var/ftp/pub/group ];then
    echo "文件上传成功"
        else
        echo "上传、创建目录删除目录部ok"
        fi 
fi   
[ -f /var/ftp/pub/group ] && echo "上传文件成功"

shell对输出流的处理-awk

1、awk介绍

在日常计算机管理中,总会有很多数据输出到屏幕或者文件,这些输出包含了标准输出、标准错误输出。默认情况下,这些信息全部输出到默认输出设备—屏幕。然而,大量的数据输出中,只有一小部分是我们需要重点关注的,我们需要把我们需要的或者关注的这些信息过滤或者提取以备后续需要时调用。早先的学习中,我们学过使用grep来过滤这些数据,使用cut、tr命令提出某些字段,但是他们都不具备提取并处理数据的能力,都必须先过滤,再提取转存到变量,然后在通过变量提取去处理,比如:

内存使用率的统计步骤

1) 通过free -m提取出内存总量,赋值给变量 memory_totle

2)通过free -m提取出n内存使用量,赋值给变量memory_use

3)通过数学运算计算内存使用率

需要执行多步才能得到内存使用率,那么有没有一个命令能够集过滤、提取、运算为一体呢?当然,就是今天我要给大家介绍的命令:awk

平行命令还有gawk、pgawk、dgawk

awk是一种可以处理数据、产生格式化报表的语言,功能十分强大。awk 认为文件中的每一行是一条记录 记录与记录的分隔符为换行符,每一列是一个字段 字段与字段的分隔符默认是一个或多个空格或tab制表符.

awk的工作方式是读取数据,将每一行数据视为一条记录(record)每条记录以字段分隔符分成若干字段,然后输出各个字段的值.

2、awk语法

awk [options] ‘[BEGIN]{program}[END]’ [FILENAME]

常用命令选项
-F fs 指定描绘一行中数据字段的文件分隔符  默认为空格
-f file 指定读取程序的文件名
-v var=value 定义awk程序中使用的变量和默认值

注意:awk 程序由左大括号和右大括号定义。 程序命令必须放置在两个大括号之间。由于awk命令行假定程序是单文本字符串,所以必须将程序包括在单引号内。
1)程序必须放在大括号内
2)程序必须要用单引号引起来

awk程序运行优先级是:
    1)BEGIN: 在开始处理数据流之前执行,可选项
    2)program: 如何处理数据流,必选项
    3)END: 处理完数据流后执行,可选项

3、awk基本应用

能够熟练使用awk对标准输出的行、列、字符串截取

学习用例
[root@zuolaoshi ~]# cat test 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

3.1)awk对字段(列)的提取

字段提取:提取一个文本中的一列数据并打印输出

字段相关内置变量

$0 表示整行文本

$1 表示文本行中的第一个数据字段

$2 表示文本行中的第二个数据字段

$N 表示文本行中的第N个数据字段

$NF 表示文本行中的最后一个数据字段

读入test每行数据并把每行数据打印出来
[root@zuolaoshi ~]# awk '{print $0}' test 
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog

打印test第六个字段
[root@zuolaoshi ~]# awk '{print $6}' test
jumps
jumps
jumps
jumps
jumps


打印test最后一个字段
[root@zuolaoshi ~]# awk '{print $NF}' test
dog
dog
dog
dog
dog

3.2)命令选项详解

-F: 指定字段与字段的分隔符

当输出的数据流字段格式不是awk默认的字段格式时,我们可以使用-F命令选项来重新定义数据流字段分隔符。比如:

处理的文件是/etc/passwd,希望打印第一列、第三列、最后一列

[root@zuolaoshi ~]# awk -F ':' '{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash
bin 1 /sbin/nologin
daemon 2 /sbin/nologin
adm 3 /sbin/nologin
lp 4 /sbin/nologin
sync 5 /bin/sync
shutdown 6 /sbin/shutdown
halt 7 /sbin/halt
mail 8 /sbin/nologin
operator 11 /sbin/nologin
games 12 /sbin/nologin
ftp 14 /sbin/nologin
nobody 99 /sbin/nologin
systemd-network 192 /sbin/nologin
dbus 81 /sbin/nologin
polkitd 999 /sbin/nologin
postfix 89 /sbin/nologin
chrony 998 /sbin/nologin
sshd 74 /sbin/nologin
ntp 38 /sbin/nologin
tcpdump 72 /sbin/nologin
nscd 28 /sbin/nologin
mysql 997 /sbin/nologin
www 996 /sbin/nologin
apache 48 /sbin/nologin
tss 59 /sbin/nologin
zabbix 995 /sbin/nologin
saslauth 994 /sbin/nologin
grafana 993 /sbin/nologin

可以看的出awk输出字段默认的分隔符也是空格

-f file: 如果awk命令是日常重复工作,而又没有太多变化,可以将程序写入文件,每次使用-f调用程序文件就好,方便,高效。

[root@zuolaoshi ~]# cat abc 
{print $1,$3,$NF}

[root@zuolaoshi ~]# awk -f abc test
1 quick dog
2 quick dog
3 quick dog
4 quick dog
5 quick dog

-v 定义变量,既然作者写awk的时候就是按着语言去写的,那么语言中最重要的要素—变量肯定不能缺席,所以可以使用-v命令选项定义变量

[root@zuolaoshi ~]# awk -v name='baism' 'BEGIN{print name}'
baism

定义了一个变量 name=baism然后调用变量读出数据

3.3)awk对记录(行)的提取

记录提取:提取一个文本中的一行并打印输出

记录的提取方法有两种:a、通过行号 b、通过正则匹配

记录相关内置变量

NR: 指定行号 number row

提取test第三行数据

指定行号为3
[root@zuolaoshi ~]# awk 'NR==3{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

指定行的第一个字段精确匹配字符串为3
[root@zuolaoshi ~]# awk '$1=="3"{print $0}' test 
3 the quick brown fox         jumps over the lazy cat . dog

3.4)awk对字符串提取

记录和字段的汇合点就是字符串

打印test第三行的第六个字段
[root@zuolaoshi ~]# awk 'NR==3{print $6}' test
jumps

4、awk程序的优先级

awk代码块的优先级

关于awk程序的执行优先级,BEGIN是优先级最高的代码块,是在执行PROGRAM之前执行的,不需要提供数据源,因为不涉及到任何数据的处理,也不依赖与PROGRAM代码块;PROGRAM是对数据流干什么,是必选代码块,也是默认代码块。所以在执行时必须提供数据源;END是处理完数据流后的操作,如果需要执行END代码块,就必须需要PROGRAM的支持,单个无法执行。

BEGIN:处理数据源之前干什么 不需要数据源就可以执行

PROGRAM: 对数据源干什么 【默认必须有】 需要数据源

END:处理完数据源后干什么 需要program 需要数据源

优先级展示
[root@zuolaoshi ~]# awk 'BEGIN{print "hello zuolaoshi"}{print $0}END{print "bye zuolaoshi"}' test
hello zuolaoshi
1 the quick brown fox jumps over the lazy cat . dog
2 the quick brown fox jumps over the lazy cat . dog
3 the quick brown fox         jumps over the lazy cat . dog
4 the quick brown fox jumps over the lazy cat . dog
5 the quick brown fox jumps over the lazy cat . dog
bye zuolaoshi

不需要数据源可以直接执行
[root@zuolaoshi ~]# awk 'BEGIN{print "hello world"}'
hello world

没有提供数据流所以无法执行成功
[root@zuolaoshi ~]# awk '{print "hello world"}'
[root@zuolaoshi ~]# awk 'END{print "hello world"}'

5、awk高级应用

awk是一门语言,那么就会符合语言的特性,除了可以定义变量外,还可以定义数组,还可以进行运算,流程控制,我们接下来看看吧。

5.1)awk定义变量和数组

定义变量

[root@zuolaoshi ~]# awk -v name='baism' 'BEGIN{print name}'
baism

[root@zuolaoshi ~]# awk 'BEGIN{name="baism";print name}'
baism

数组定义方式: 数组名[索引]=值

定义数组array有两个元素分别是100200打印数组元素
[root@zuolaoshi ~]# awk 'BEGIN{array[0]=100;array[1]=200;print array[0],array[1]}'
100 200

[root@zuolaoshi ~]# awk 'BEGIN{a[0]=100;a[1]=200;print a[0]}'
100
[root@zuolaoshi ~]# awk 'BEGIN{a[0]=100;a[1]=200;print a[1]}'
200

5.2)awk运算

  1. 赋值运算 =
  2. 比较运算 > >= == < <= !=
  3. 数学运算 + - * / % ** ++ –
  4. 逻辑运算 && || !
  5. 匹配运算 ~ !~ 精确匹配 == !=

a.赋值运算:主要是对变量或者数组赋值,如:

变量赋值 name=‘baism’ school=‘zuolaoshi’

数组赋值 array[0]=100

[root@zuolaoshi ~]# awk -v name='baism' 'BEGIN{print name}'
baism

[root@zuolaoshi ~]# awk 'BEGIN{school="zuolaoshi";print school}'
zuolaoshi

[root@zuolaoshi ~]# awk 'BEGIN{array[0]=100;print array[0]}'
100

b.比较运算,如果比较的是字符串则按ascii编码顺序表比较。如果结果返回为真则用1表示,如果返回为假则用0表示

ascii

[root@zuolaoshi ~]# awk 'BEGIN{print "a" >= "b" }'
0
[root@zuolaoshi ~]# awk 'BEGIN{print "a" <= "b" }'
1

[root@zuolaoshi ~]# awk '$1>4{print $0}' test
5 the quick brown fox jumps over the lazy cat . dog
[root@zuolaoshi ~]# awk 'BEGIN{print 100 >= 1 }'
1
[root@zuolaoshi ~]# awk 'BEGIN{print 100 == 1 }'
0
[root@zuolaoshi ~]# awk 'BEGIN{print 100 <= 1 }'
0
[root@zuolaoshi ~]# awk 'BEGIN{print 100 < 1 }'
0
[root@zuolaoshi ~]# awk 'BEGIN{print 100 != 1 }'
1

c.数学运算

[root@zuolaoshi ~]# awk 'BEGIN{print 100+3 }'
103
[root@zuolaoshi ~]# awk 'BEGIN{print 100-3 }'
97
[root@zuolaoshi ~]# awk 'BEGIN{print 100*3 }'
300
[root@zuolaoshi ~]# awk 'BEGIN{print 100/3 }'
33.3333
[root@zuolaoshi ~]# awk 'BEGIN{print 100**3 }'
1000000
[root@zuolaoshi ~]# awk 'BEGIN{print 100%3 }'
1
[root@zuolaoshi ~]# awk -v 'count=0' 'BEGIN{count++;print count}'
1
[root@zuolaoshi ~]# awk -v 'count=0' 'BEGIN{count--;print count}'
-1

d.逻辑运算

与运算:真真为真,真假为假,假假为假
[root@zuolaoshi ~]# awk 'BEGIN{print 100>=2 && 100>=3 }'
1
[root@zuolaoshi ~]# awk 'BEGIN{print 100>=2 && 1>=100 }'
0

或运算:真真为真,真假为真,假假为假
[root@zuolaoshi ~]# awk 'BEGIN{print 100>=2 || 1>=100 }'
1
[root@zuolaoshi ~]# awk 'BEGIN{print 100>=200 || 1>=100 }'
0

非运算
[root@manage01 resource]# awk 'BEGIN{print ! (100>=2)}'
0

e.匹配运算

[root@zuolaoshi ~]# awk -F ':' '$1 ~ "^ro" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@zuolaoshi ~]# awk -F ':' '$1 !~ "^ro" {print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:997:User for polkitd:/:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
mysql:x:997:995::/home/mysql:/sbin/nologin
www:x:996:994::/home/www:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
zabbix:x:995:993:Zabbix Monitoring System:/var/lib/zabbix:/sbin/nologin
saslauth:x:994:76:Saslauthd user:/run/saslauthd:/sbin/nologin
grafana:x:993:992:grafana user:/usr/share/grafana:/sbin/nologin

5.3)awk 环境变量

变量 描述
FIELDWIDTHS 以空格分隔的数字列表,用空格定义每个数据字段的精确宽度
FS 输入字段分隔符号 数据源的字段分隔符 -F
OFS 输出字段分隔符号
RS 输入记录分隔符
ORS 输出记录分隔符号
FIELDWIDTHS:重定义列宽并打印,注意不可以使用$0打印所有,因为$0是打印本行全内容,不会打印你定义的字段
[root@zuolaoshi ~]# awk 'BEGIN{FIELDWIDTHS="5 2 8"}NR==1{print $1,$2,$3}' /etc/passwd
root: x: 0:0:root

FS:指定数据源中字段分隔符,类似命令选项-F
[root@zuolaoshi ~]# awk 'BEGIN{FS=":"}NR==1{print $1,$3,$NF}' /etc/passwd
root 0 /bin/bash

OFS:指定输出到屏幕后字段的分隔符
[root@zuolaoshi ~]# awk 'BEGIN{FS=":";OFS="-"}NR==1{print $1,$3,$NF}' /etc/passwd
root-0-/bin/bash

RS:指定记录的分隔符
[root@zuolaoshi ~]# awk 'BEGIN{RS=""}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5

将记录的分隔符修改为空行后,所有的行会变成一行,所以所有字段就在一行了。

ORS:输出到屏幕后记录的分隔符,默认为回车
[root@zuolaoshi ~]# awk 'BEGIN{RS="";ORS="*"}{print $1,$13,$25,$37,$49}' test
1 2 3 4 5*[root@zuolaoshi ~]# 

可以看出,提示符和输出在一行了,因为默认回车换成了*

5.4)流程控制

if判断语句

学习用例
[root@zuolaoshi ~]# cat num
1
2
3
4
5
6
7
8
9

单if语句
打印$1大于5的行
[root@zuolaoshi ~]# awk '{if($1>5)print $0}' num
6
7
8
9

if...else语句
假如$1大于5则除以2输出否则乘以2输出
[root@zuolaoshi ~]# awk '{if($1>5)print $1/2;else print $1*2}' num
2
4
6
8
10
3
3.5
4
4.5

for循环语句

学习用例
[root@zuolaoshi ~]# cat num2
60 50 100
150 30 10
70 100 40

将一行中的数据都加起来  $1+$2+$3
[root@zuolaoshi ~]# awk '{sum=0;for (i=1;i<4;i++){sum+=$i}print sum}' num2
210
190
210

如果看的不明白可以看下面格式
[root@zuolaoshi ~]# awk '{
> sum=0
> for (i=1;i<4;i++) {
>     sum+=$i
> }
> print sum
> }' num2
210
190
210

while循环语句–先判断后执行

学习用例
[root@zuolaoshi ~]# cat num2
60 50 100
150 30 10
70 100 40

将文件中的每行的数值累加和大于或等于150就停止累加
[root@zuolaoshi ~]# awk '{sum=0;i=1;while(sum<150){sum+=$i;i++}print sum}' num2
210
150
170



如果看的不明白可以看下面格式
[root@zuolaoshi ~]# awk '{                                                 
sum=0
i=1
while (sum<150) {
   sum+=$i
   i++
}
print sum
}' num2
210
150
170

do…while循环语句–先执行后判断

学习用例
[root@zuolaoshi ~]# cat num2
60 50 100
150 30 10
70 100 40

将文件中的每行的数值累加和大于或等于150就停止累加
[root@zuolaoshi ~]# awk '{sum=0;i=1;do{sum+=$i;i++}while(sum<150);print sum}' num2
210
150
170
如果看的不明白可以看下面格式
[root@zuolaoshi ~]# awk '{
> sum=0
> i=1
> do {
> sum+=$i
> i++
> }while (sum<150)
> print sum
> }' num2
210
150
170

循环控制语句

break 跳出循环,继续执行后续语句

学习用例
[root@zuolaoshi ~]# cat num2
60 50 100
150 30 10
70 100 40

累加每行数值和大于150停止累加
[root@zuolaoshi ~]# awk '{
> sum=0
> i=1
> while (i<4){
>    sum+=$i
>    if (sum>150){
>       break
>    }
>    i++
> }
> print sum
> }' num2
210
180
170

处理海量日志对每一个运维来说都非常的头疼,日志分析我们首先需要把需要的数据从海量的日志中匹配出来,降低数据量,然后在分析这些日志。那么从海量的日志中把我们需要的日志找出来就需要我们写一个公式来匹配,那么如何才能写一个这样的公式呢?

这节课我就给大家说说正则表达式,看起来比较高大上,其实就是通过给定的符号生成一个字符串匹配的公式,通过该公式把需要的数据匹配出来。

比如

  • 匹配正确的IP地址
  • 匹配正确的e-mail地址

正则表达式

1、正则表达式介绍

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。

许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。

正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。

支持正则表达式的程序如:locate |find| vim| grep| sed |awk

正则表达式是一个三方产品,被常用计算机语言广泛使用,比如:shell、PHP、python、java、js等!

[root@manage01 ~]# locate sko
locate: 无法执行 stat () `/var/lib/mlocate/mlocate.db': 没有那个文件或目录

使用updatedb生成数据文件
[root@manage01 ~]# updatedb

2、正则表达式特殊字符

定位符使用技巧:同时锚定开头和结尾,做精确匹配;单一锚定开头或结尾或者不锚定的,做模糊匹配。

定位符 说明
^ 锚定开头 ^a 以a开头 默认锚定一个字符
$ 锚定结尾 a$ 以a结尾 默认锚定一个字符

测试案例

1精确匹配  以a开头c结尾的字符串
[root@zuolaoshi ~]# egrep "^ac$" file 
ac

2模糊匹配  以a开头
[root@zuolaoshi ~]# egrep "^a" file 
ac
ab
abbc
abcc
aabbcc
abbbc
abbbbbc
acc
abc
asb
aa
a_c
aZc
aAAAAc
a c
abababab
a3c


3模糊匹配 以c结尾的字符串
[root@zuolaoshi ~]# egrep "c$" file 
ac
abbc
abcc
aabbcc
abbbc
abbbbbc
acc
abc
a_c
aZc
aAAAAc
a c
ccc
a3c

匹配符:匹配字符串

匹配符 说明
. 匹配除回车以外的任意一个字符
( ) 字符串分组
[ ] 定义字符类,匹配括号中的一个字符
[ ^ ] 表示否定括号中出现字符类中的字符,取反。
\ 转义字符
|

测试案例

1精确匹配  以a开头c结尾  中间任意  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a.c$" file 
acc
abc
a_c
aZc
a c
a3c

2模糊匹配  以cc结尾的字符串   因为$只能锚定单个字符如果是一个字符串就需要用()来做定义
[root@zuolaoshi ~]# egrep "(cc)$" file 
abcc
aabbcc
acc
ccc

3精确匹配  以a开头c结尾  中间是a-z,0-9  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[a-z0-9]c$" file 
acc
abc
a3c

4)精确匹配  以a开头c结尾  中间不包含a-z,0-9  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[^a-z0-9]c$" file 
a_c
aZc
a c

5精确匹配  以e开头f结尾  中间是*  长度为三个字节的字符串  e*f
[root@zuolaoshi ~]# egrep "^e\*f$" file 
e*f

6精确匹配 以a开头b或c结尾  中间是任意  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a.(b|c)$" file 
acc
abc
asb
a_c
aZc
a c
a3c

限定符:对前面的字符或者(字符串)出现的次数做限定说明

限定符 说明
* 某个字符之后加星号表示该字符不出现或出现多次 a (ab)
与星号相似,但略有变化,表示该字符出现一次或不出现
+ 与星号相似,表示其前面字符出现一次或多次,但必须出现一次
{n,m} 某个字符之后出现,表示该字符最少n次,最多m次
{m} 正好出现了m次

测试案例

1精确匹配 以a开头 c结尾 中间是有b或者没有b 长度不限的字符串
[root@zuolaoshi ~]# egrep "^ab*c$" file 
ac
abbc
abbbc
abbbbbc
abc

2精确匹配 以a开头 c结尾 中间只出现一次b或者没有b的字符串
[root@zuolaoshi ~]# egrep "^ab?c$" file 
ac
abc

3精确匹配 以a开头 c结尾 中间是有b且至少出现一次 长度不限的字符串
[root@zuolaoshi ~]# egrep "^ab+c$" file 
abbc
abbbc
abbbbbc
abc

4精确匹配 以a开头 c结尾 中间是有b且至少出现两次最多出现四次 长度不限的字符串
[root@zuolaoshi ~]# egrep "^ab{2,4}c$" file 
abbc
abbbc

5精确匹配 以a开头 c结尾 中间是有b且正好出现三次的字符串
[root@zuolaoshi ~]# egrep "^ab{3}c$" file 
abbbc

6) 精确匹配 以a开头 c结尾 中间是有b且至少出现一次的字符串
[root@zuolaoshi ~]# egrep "^ab{1,}c$" file 
abbc
abbbc
abbbbbc
abc

3、正则表达式POSIX字符

posix字符一次只匹配一个范围中的一个字节

特殊字符 说明
[:alnum:] 匹配任意字母字符0-9 a-z A-Z
[:alpha:] 匹配任意字母,大写或小写
[:digit:] 数字 0-9
[:graph:] 非空字符( 非空格控制字符)
[:lower:] 小写字符a-z
[:upper:] 大写字符A-Z
[:cntrl:] 控制字符
[:print:] 非空字符( 包括空格)
[:punct:] 标点符号
[:blank:] 空格和TAB字符
[:xdigit:] 16 进制数字
[:space:] 所有空白字符( 新行、空格、制表符)

测试案例

注意[[ ]]  双中括号的意思:  第一个中括号是匹配符[] 匹配中括号中的任意一个字符第二个[]是格式 [:digit:]

1精确匹配  以a开头c结尾  中间a-zA-Z0-9任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:alnum:]]c$" file
acc
abc
aZc
a3c

2精确匹配  以a开头c结尾  中间是a-zA-Z任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:alpha:]]c$" file
acc
abc
aZc


3精确匹配  以a开头c结尾  中间是0-9任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:digit:]]c$" file
a3c

4精确匹配  以a开头c结尾  中间是a-z任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:lower:]]c$" file
acc
abc


4精确匹配  以a开头c结尾  中间是A-Z任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:upper:]]c$" file
aZc

5精确匹配  以a开头c结尾  中间是非空任意字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:print:]]c$" file
acc
abc
a_c
aZc
a c
a3c

6精确匹配  以a开头c结尾  中间是符号字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:punct:]]c$" file
a_c

7精确匹配  以a开头c结尾  中间是空格或者TAB符字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:blank:]]c$" file
a c

类似
[root@zuolaoshi ~]# egrep "^a[[:space:]]c$" file
a c

8精确匹配  以a开头c结尾  中间是十六进制字符  长度为三个字节的字符串
[root@zuolaoshi ~]# egrep "^a[[:xdigit:]]c$" file
acc
abc
a3c

说明:特殊字符和POSIX字符是两套字符,都可以完成需要的匹配,大家学习的时候最少要记住一套字符并熟练应用。

4、练习案例

1IP匹配公式
grep '^((25[0-5]|2[0-4][[:digit:]]|[01]?[[:digit:]][[:digit:]]?).){3}(25[0-5]|2[0-4][[:digit:]]|[01]?[[:digit:]][[:digit:]]?)$' --color ip_base
2E-MAIL地址匹配公式