Centos7学习笔记(四)——grep、sed、awk
1、grep——print lines matching a pattern打印匹配内容的行(多行)
命令格式:grep [参数] 过滤匹配内容 [文件名]或者grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
常用参数:
-v 取反过滤 ◆◆
-i 忽略大小写 ◆◆(find 命令可以用-iname)
-n 对过滤后的内容,显示源文件行号 ◆◆
-w 按单词为单位过滤。◆◆
-o只输出匹配的内容。◆◆
-E使用扩展规则
-r递归查找
-l只显示文件名
-r例题:
在当前目录下找到所有.cc的文件中包含“host”字样的文件
find ./ -type f -name ".cc" |xargs grep "host" #《=========常规写法
grep -r "host" ./ #《======grep写法
例题:新建num1.txt文件并在其中键入1-30数字序列,并显示数字10-20的行。
第一问:seq 30 >num1.txt
或 echo -e {1..30}"\n" >num1.txt
第二问:head -20 num1.txt|tail -11
或者grep 10 -B 20 num1.txt
或者grep 10 -A 10 num1.txt
或者grep 15 -C 5 num1.txt
例题:写出删除文件abc.txt中的空行(注意,有3种写法)(空格开头没内容的也算空行)
第一种写法:grep -v "^$" abc.txt #《======初级
第二种写法:egrep -v "^$|^ +$" abc.txt #《=====中级
第三种写法:egrep -v "^ *$" abc.txt #《========高级
sed -nr '(^ *$)d' abc.txt #《=========sed的写法
2、sed——stream editor for filtering and transforming text过滤和转换文本的流编辑器
命令格式:sed [参数选项] [sed内置命令字符] [文件名]
常用参数:
-n 取消默认sed的输出,常与sed内置命令字符p连用
-i 直接修改文件内容,而不是输出到终端。可以用-i.bak的形式备份(在修改前)
-f 引用一个脚本文件内容到命令中执行
-r引用扩展正则
PS:(-i参数与其他参数同时用时,要放其他参数最后。否则会把其他参数,当成备份文件格式。另外,i和n不能同时用,否则造成清空其他行内容的效果。)
sed内置命令字符
s——替换sub-expressions,s是针对字符串处理。,g作为s替换的修饰符,还要其他,比如p,i,I等修饰符。
d——删除(delete)
a——在某一行后追加(增加)内容(append),用法:na(n是数字,代表具体行数)
i——在某一行前插入(增加)内容(insert),用法:ni(n是数字,代表具体行数)
c——替换某一行内容。c是针对行内容替换。
I——忽略正则匹配部分的大小写
q——立即退出当前sed程序,使其不再执行后面的命令,也不在读取后面的行。还有Q也是一样的作用,两者的区别在于,q在退出前还会自动输出模式空间(已读取到数据的内存空间)的内容,而Q不会输出。具体见后面例子
n——手动读取下一行
例题:1、输出文件的第2、3行(查)

2、查看参数-n的作用(查)

3、查看文件中含mail的行(查)

4、删除文件中含list单词的行(删)

5、替换文件中的root为study字串(改)

6、将文件中的ssh替换成ftp,mail替换成tftp(改)

这里,主要是用-e增加多项替换操作。(如果需要真实修改,需要增加-i)
7、将第一行内容替换为I test.

8、删除指定的行
其实,这里可以用‘1d;4d;6d;8d’写法,不需要-e
[root@studylinux ~/test]#sed '1,5d' statetab #《==== 这里是删除第1到5行 # # Examples: # # /root # /etc/ftp # /var/spool/tftp # 这里还有一种情形, m~n 表示从m行开始每隔n行就再取一行,也就是取行号满足m+(x*n)其中n>=0的行。因此,要取所有奇数行,应写为1~2,取所有偶数行,写为2~2。取从第4行开始,每隔3行的行,写为4~3。单取50行,写为50~0。
删除a.sh中包含"#"开头的注释行,但第一行的#!/bin/bash不删除。
sed '/^#/{1!d}' a.sh9、在第2行后增加I test.和I study again.这样两行。

另一个例子:在第四行之后追加内容
这里,最后一个\t后的\符号,可加可不加,但是,如果在\后直接回车执行了,那么效果完全不同

10.用sed取出stat /etc/hosts结果中的644(文件权限值)。

注意,这里的思路:1、要取644值,它在stat /etc/hosts输出结果的第4行。
2、取值并打印输出,对sed来说,必然是要用s###g,来匹配对应的内容,然后用p来打印。
3、因为这里必然是0-9之间的数字,并且重复3次,所以,用扩展正则的()取整体,然后用\1来反向替代。
[root@studylinux ~/test]#stat /etc/hosts|sed -nr 's#.*\(0(.*)/-.*$#\1#gp' #《====通过stat文件的方式,取文件的整个权限值 664
[root@studylinux ~/test]#ip add |sed -nr 's#.*inet (.*)/.*brd.*$#\1#gp' #《=====取ip add输出后的ip地址值 192.168.205.129 ifconfig |sed -nr '2s#^.*inet (.*) netm.*$#\1#gp' #《=======取ifconfig输出后的IP地址值
11、搜索脚本a.sh,当搜索到使用了"."或"source"命令加载环境配置脚本时就输出并立即退出。
sed -n -r '/^[ \t]*(\.|source) /{p;q}' a.sh #《=====这里的q,就是在匹配成功后,立刻退出sed循环12、搜索出httpd.conf中"DocumentRoot"开头的行的行号,允许有前导空白字符。
sed -n '/^[ \t]*DocumentRoot/{p;=}' httpd.conf #《=======这里的=,是用来输出sed命令所处理的当前行行号。
如果"="命令前没有"p"输出命令,且没有使用"-n"选项,则是输出在Document所在行的前一行,因为SCRIPT最后的自动输出动作也有输出流。13、搜索a.txt中包含"redirect"字符串的行以及其下一行,并输出。
sed -n '/redirect/{p;n;p}' a.txt
再例如下面的命令。
echo -e "abc\ndef\nxyz" | sed '/abc/{n;=;p}'
abc
2
def
def
xyz从结果中可以分析出,"n"读取下一行前输出了"abc",然后立即读入了下一行,所以输出的行号是2而不是1,因为这时候行号计数器已经读取了下一行,随后命令"p"输出了该模式空间的内容,输出后还有一次自动输出的隐含动作,所以"def"被输出了两次。
14、删除a.sh中所有"#"开头(可以包括前导空白)的注释符号"#",但第一行"#!/bin/bash"不处理。
sed -i '2,$s/^[ \t]*#//' a.sh #《======这里,注意用的是“2,$”,在sed中,$表示它所处理的最后一行的行号。但是,不能用“$-1”这样的方式来表示倒数第二行的行号,为何? 因为sed只有在处理到最后一行时,才知道它是最后一行,然后在内存中才能赋值最后一行行号值给$,在此之前,$是没有值的,又怎么能$-1呢?
15、将a.sh中"cmd1 && cmd2 || cmd3"的cmd2和cmd3命令对调个位置。
sed 's%&&\(.*\) ||\(.*\)%\&\&\2 ||\1%' a.sh #《=============这里使用了"%"代替"/",且在REPLACEMENT部分对"&"进行了转义,因为该符号在REPLACEMENT中时表示的是引用REGEXP所匹配的所有内容。
16、批量修改文件格式。
ls *.jpg
ls *.jpg|xargs -n1|sed -nr 's#(.*)jpg#mv \1jpg \1txt #gp'|bash

17、取反内容——取出/etc/ssh/sshd_config,不包括空行和注释行的内容。
sed -nr '/^$|^#/!p' /etc/ssh/sshd_config

3、awk——pattern scanning and processing language 样式(模式)扫描和处理语言
用法:awk [option] 'pattern{action}' file ...
***重点:pattern,可以翻译做“条件”。一般用关系表达式来作为条件,比如
x > 10 判断变量x是否大于10
x == y 判断变量x是否等于变量y
A ~ B 判断字符串A中是否包含能匹配B表达式的子字符串
A !~ B 判断字符串A中是否不包含能匹配B表达式的子字符串
具体的“条件”可以按下表分类:
| 条件类型 | 条件 | 说明 |
|---|---|---|
| awk保留字 | BEGIN | 在awk程序一开始时,尚未读取任何数据之前执行,BEGIN后的动作只在程序开始时执行一次 |
| END | 在awk程序处理完所有数据,即将结束时执行,END后的动作只在程序结束时执行一次 | |
| 关系运算符 | > | 大于 |
| < | 小于 | |
| >= | 大于等于 | |
| <= | 小于等于 | |
| != | 不等于 | |
| == | 等于。判断两个值是否相等。 | |
| A~B | 判断字符串A中是否包含能匹配B表达式的子字符串 | |
| A!~B | 判断字符串A中是否不包含能匹配B表达式的子字符串 | |
| 正则表达式 | /正则/ | 如果在“//”中可以写入字符,也可以写入正则表达式 |
以上条件或者判断,可以灵活使用,组合使用。甚至也可以结合awk的内置函数来做判断。以下是awk的内置函数:
| awk内置变量 | 作用 |
|---|---|
| $0 | 代表awk当前所读入的整行数据,awk仍然是按行处理数据 |
| $n | n是数字,代表目前读入行的第n个字段 |
| NF | 当前行拥有的字段总数 |
| NR | 当前awk所处理的行,是总数据的第几行。 |
| FS | 用户自定义分隔符。awk默认分隔符为空格,如果想用其他分隔符,就要用FS变量定义 |
| ARGC | 命令行参数个数 |
| ARGV | 命令行参数数组 |
| FNR | 当前文件中的当前记录数(对输入文件起始为1) |
| OFMT | 数值的输出格式(默认为%.6g) |
| OFS | 输出字段的分隔符(默认为空格) |
| ORS | 输出记录的分隔符(默认为换行符) |
| RS | 输入记录的分隔符(默认为换行符) |
这里需要注意的是:如果用FS来自定义分隔符,那么,需要尽量用BEGIN{FS=":"}的形式,不然容易出现第一行处理不了的情况。

action,就是动作。可以是print或printf格式化输出。也可以是流程控制语句(if..else、for..do等)
awk中,'pattern{action}'可以跟多段,即awk [option] 'pattern{action}' 'pattern{action}' file这样的形式。
参数(opiton):
-F指定分隔符(可以是字符串、符号或者是正则表达式)

注意,这里有一个“action”——print,但没有pattern。
上图中,$1代表第一列,$2的话,代表第二列以此类推。但有特殊的:
$0 —— 代表了整行(或者说是一行的所有列)
$NF —— 代表了最后一列
$(NF-1) —— 倒数第二列,这里可以类推,如$(NF-3)

NR——已经读出的记录数,就是行号,从1开始

下例:筛选出有adm字符串的行

下例:删除带有root的行


4、printf
语法:printf '输出类型输出格式' 输出内容
参数:%ns——n是数字,指代输出几个字符。
%ni——n是数字,指代输出几个整数
%m.nf——m和n都是数字,指代输出的整数位和小数位。比如%8.2f,指总共输出8位,其中6位是整数,2位是小数。
\a——表示输出警告声音。
\b——表示输出退格键,也即是输出backspace键
\f——清除屏幕
\n——换行
\r——回车键,即Eneter键
\t——水平制表符,即tab键
\v——垂直制表符,即tab键
printf命令不能直接跟文件名,基本上,只用在awk命令中。
printf '%s\t%s\t%i\t%i\t%8.2f \n' $(cat test.txt)
(print和printf区别——print只能用于awk语句,区别在于print已经格式化好了,用\n去分隔行。printf不行,需要自行添加\n去分隔每一行)
5、字符串操作函数
gsub(r, s [, t])
r是被替换部分,s是替换后部分。具体看下图。注意每部分需要被//或者""起来

6、awk中变量的使用

变量名之前,不需要加$符号,如果加了$符号,基本都等同于$0。如图

如果需要申明一串字符为,必须给其加双引号"",否则字符串会被视为变量。(如上面gsub中,被替换后的内容,若为字符串,必须加双引号)
7、利用awk数组,统计/var/log/secure文件中,每个“Failed password”登录失败的IP。

awk '/Failed password/{count[$(NF-3)]++}END{for (IP in count) print count[IP],IP}' /var/log/secure
思路:先grep 'Failed password' /var/log/secure|head,确定输出内容的格式。

然后改用awk,确定需要输出的IP为$(NF-3)列。

再将整个$(NF-3)列放入一个count[]数组中,就可以参与运算了。
统计nginx的access.log日志中IP地址访问次数,并从高到低排序。
awk '{count[$1]++}END{for (IP in count) print count[IP],IP}' /var/log/nginx/access.log|sort -nrk1
以下是各类三剑客实例:
C6下ifconfig eth0,取IP地址值
ifconfig eth0|awk 'NR==2{print $2}'|awk -F ":" '{print $2}' #《===== 笨办法
ifconfig eth0|awk -F "(addr:)|(Bcast)" 'NR==2{print $2}'
ifconfig eth0|awk -F "[ :]+" 'NR==2{print $4}' #《====== 这里,用了多分隔符,分别是用空格和:符号做分隔,由于inet前有很多空格,且addr也作为了一段,print的变$3了ip add下,取IP地址值
ip add|awk 'NR==8{print $2}'|awk -F "/" '{print $1}' #《===== 笨办法
ip add|awk -F "(inet )|(/)" 'NR==8{print $2}'
ip add|awk -F "[ /]+" 'NR==8{print $3}' #《====== 这里,用了多分隔符,分别是用空格和/符号做分隔,由于ip add前有很多空格,那么用2种分隔符后,print的变$3了C7下,ifconfig eth0,取IP地址值
ifconfig eth0|awk 'NR==2{print $2}'
ifconfig eth0 |sed -nr 's#^.*inet (.*) netm.*$#\1#gp'ip add下,取IP地址值
ip add|awk 'NR==9{print $2}'|awk -F "/" '{print $1}' #《======= 笨办法
ip add|awk -F "(inet )|(/)" 'NR==9{print $2}'
ip add|awk -F "[ /]+" 'NR==9{print $3}' #《====== 这里,用了多分隔符,分别是用空格和/符号做分隔,由于ip add前有很多空格,那么用2种分隔符后,print的变$3了
ip add|sed -nr '9s#.*inet (.*)\/.*$#\1#gp'cat /etc/passwd |grep -v ":/bin/bash$"
cat /etc/passwd |grep -w "^rpc" |cut -d: -f 7
grep "rpc" /etc/passwd |awk -F ":" '{print $NF}'
cat /etc/passwd |cut -d: -f1,3 |grep -w "[1-9][0-9]\{,2\}$"

cat /etc/passwd |grep -w "^\([^:]*\):.*/\1$"

df |grep "^/dev/sd"|grep -wo "[0-9]\+%"|sort -nr
cat test | grep -o "[[:lower:]]"|sort |uniq -c|sort -nr |tr -s ' ' | cut -d " " -f 3 |tr -d '\n'
18、如何在系统添加“test1test2、...test30"这样的30个用户,并把它们密码都修改为”123456"
seq 30|sed -nr 's#(.*)#useradd test\1#gp'|bash
seq 30|sed -nr ‘s#(.*)#echo "123456"|passwd --stdin test\1#gp’|bash #《=======两条命令的实现方式,太麻烦
echo test{1..30}|xargs -n1|sed -nr 's#(.*)#useradd \1;echo "123456"|passwd --stdin \1#gp' |bash #《=========一条命令的实现方式
19、有以下文本文件chengji.txt
cat chengji.txt 序号 姓名 成绩1 补考成绩1 成绩2 补考成绩2 成绩3 上机成绩 身高(cm) 1 曾庆虎 42 96 118 150 91 100 11 2 王昌斌 57 98 80 148.5 70 100 102 3 苏威鹏 86 100 92 148 79.5 100 170 4 王晓 68.5 100 104 149 84 100 123 5 刘明 54 100 139.5 77 92 100 184 6 苏旭波 82 95 60.5 147 85 100 102 7 郭伟 70 100 125 148 76.5 100 211 8 郭俊宁 88 100 145 99 80 100 0.9 9 张刚 43 100 74.5 150 76.5 100 21 10 贺子星 71 100 52.5 145 79 95 65
1)请将序号5人员的历次成绩取出,并按从高到低排序。
grep "^5" chengji.txt|xargs -n1|tail -7|head -6|sort -nr
20、假定系统/dev/sda3是根目录,取出根目录磁盘占有率的整数值。
df -h |grep "/dev/sda3"|awk -F "[ %]+" '{print $5}'
df -h|grep "/dev/sda3"|awk '{print $5}'|cut -d "%" -f 1
复习一点:
在a.txt文本中过滤xxx字符串,分别用grep、sed、awk的写法
grep "xxx" a.txt
sed -n '/xxx/p' a.txt
awk '/xxx/{print}' a.txt
问题2:删除字符串xxx
grep -v "xxx" a.txt
sed '/xxx/d' a.txt
awk '!/xxx/{print}' a.txt
问题3:将xxx替换为yyy
sed -n 's/xxx/yyy/gp' a.txt
awk '{gsub(/xxx/,"yyy");print $0}' a.txt #《=========这里,用了awk的内置函数gsub
awk提供了许多强大的字符串函数,见下表:
awk内置字符串函数
gsub(r,s) 在整个$0中用s替代r
gsub(r,s,t) 在整个t中用s替代r
index(s,t) 返回s中字符串t的第一位置
length(s) 返回s长度
match(s,r) 测试s是否包含匹配r的字符串
split(s,a,fs) 在fs上将s分成序列a
sprint(fmt,exp) 返回经fmt格式化后的exp
sub(r,s) 用$0中最左边最长的子串代替s
substr(s,p) 返回字符串s中从p开始的后缀部分
substr(s,p,n) 返回字符串s中从p开始长度为n的后缀部

