目录
一, awk
基本句式
过滤记录
指定分隔符
特殊关键字:
输出到不同的文件
和环境变量的交互
二, grep
三, sed
四, sort 和 uniq
五, 实战
处理以下文件内容, 将域名取出并进行计数排序, 如处理:
awk 例子
Linux 中很多文本工具都使用到了正则表达式, 正则表达式可以极大的简化 Linux 系统管理工作, 因为网上有很多正则相关的教程, 所以这里不再讲述, 我当时看的是菜鸟的正则表达式 https://www.runoob.com/regexp/regexp-syntax.html , 看个一下午在实验几遍基本就会了, 除了正向肯定预查, 反向肯定预查这几个比较复杂一些, 其他都是非常简单的, 很多时候记不住也可以查询网上对着写, 并不需要你实时记住. 这里主要谈谈 awk 等用到正则表达式的文本处理工具.
一, awk
awk 的指令必须包含在单引号中.
基本句式
awk -F'指定输入分隔符' 'BEGIN{做一些初始化工作} 一些过滤条件 {针对每行的工作}... END{最后收尾工作}'
中间的处理块可以有多个, 通过过滤条件单每行都会走一遍过滤条件, 其中 BEGIN 和 END 边只会执行一遍
过滤记录
- awk '$3==0 && $6=="LISTEN" ' netstat.txt
- awk '$3==0 && $6=="LISTEN"|| NR==1' netstat.txt
指定分隔符
awk -F: '{print $1,$3,$6}' /etc/passwd
等价于
awk 'BEGIN{FS=":"} {print $1,$3,$6}' /etc/passwd
awk -F '[;:]' 指定多个分隔符
awk -F: '{print $1,$3,$6}' OFS="\t" /etc/passwd
指定输出分隔符
需要注意的是上面 print $1,$3,$6 中的, 被替换成了分隔符, 如果是 print $1$3$6 则中间没有分隔符
特殊关键字:
NR 目前处理的行号
NF 当前处理的行一共用到的字段数目
FNR 目前处理的文件的行号 (当处理多个文件时, NR 会不停的累加, 但如果是 FNR 则在处理新文件是从 1 开始)
FILENAME 文件名
$0 当前整行
FS 输入字段分隔符 默认是空格或 Tab
RS 输入记录分隔符 默认为换行符
OFS 输出字段分隔符 默认是空格或 Tab
ORS 输出记录分隔符 默认为换行符
正则
普通匹配:
awk'/hello/ {print}' test.sh
匹配取反:
awk '!/hello/ {print}' test.sh
同时匹配:
awk '/hello/ && /world/ {print}' test.sh
或者匹配:
awk '/hello/ || /world/ {print}' test.sh
也可以写成
awk '/hello|world/ {print}' test.sh
指定列匹配:
awk '$5 ~ /hello/ {print}' test.sh
指定列匹配取反:
awk '$5 !~ /hello/ {print}' test.sh
输出到不同的文件
- $ awk 'NR!=1{if($6 ~ /TIME|ESTABLISHED/) print>"1.txt"; else if($6 ~ /LISTEN/) print>"2.txt"; else print>"3.txt"}' netstat.txt
- awk 'NR!=1{print> $6}' netstat.txt
其实使用了 > 重定向, 上例子使用了 if 语句
统计数据:
awk 'NR!=1{a[$6]++;} END {for (i in a) print i", "a[i];}' netstat.txt
行数筛选, 开头和结尾的条件使用, 分隔: awk '/test1/,/test2/ {print}' test.txt
和环境变量的交互
- $ x=5
- $ y=10
- $ export y
- $ echo $x $y
- 5 10
- $ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON["y"]}' OFS="\t" score.txt
- Marry 2143 78 89 87
- Jack 2321 66 83 55
- Tom 2122 48 82 81
- Mike 2537 87 102 105
- Bob 2415 40 62 72
二, grep
参数列表:
-w 匹配整个单词
-s 忽略文件不存在等报错
-l 仅列出匹配文件列表
-L 仅列出不匹配文件列表
-A 显示后的行数 如 - 1 匹配行的后 1 行
-B 显示前的行数 如 - 1 匹配行的前 1 行
-number 显示前后的行数 如 - 1 匹配行的前后 1 行
-n 打印行数
-c 仅显示个数
-v 反向
-o 仅显示匹配的内容
-E 则表示要使用 EREs
-P 则表示要使用 PREs
grep 主要就是一个正则表达式的使用, 其中需要注意正则有三种 BREs,EREs 和 PREs. 前两种不支持非贪婪匹配. grep 默认是 BREs, 所以他?,+,|,{,},(,)
这种字符都需要用 \ 转义, 另外他不支持 \ s,\S,\D,\d,\n 等字符.
三, sed
sed 命令在自动化脚本编写的过程还是经常用到的.
基本句式: sed -nfei [操作]
操作: n1,n2 动作
动作:
d: 删除
s: 替换, 行内替换, 行内匹配的字符串, 如 hello world 该行替换 hello 为 hi 变成 hi world
a 和 i: a 增加在匹配的后面增加行 i 增加在匹配的前面增加行
c : 替换, 针对整行替换
例子:
sed -e 's/hello/hi/g'
: 替换文本,-e 可以省略
sed -e '1,2s/hello/hi/g' -e '3,4s/world/man/g
: 等价于
- sed -e '1,2s/hello/hi/g;3,4s/world/man/g
- sed s/hello \(world\)/\1 hi/g'
: 群组匹配, 可以使用 \ n 选择前面的群组
四, sort 和 uniq
sort 参数
-r: 默认升序,-r 表示反序
-u: 移除重复
-o: 重定向到文件, 注意
sort test.txt>test.txt
不可用, 因为 > 是想清空文件, 所以会导致文件在排序之前就清空了
-n: 默认按字符排序, 如 10 小于 2,-n 表示按数字排序
-t: 指定分隔符
-k: 指明用哪一列来做排序
-b: 忽略每行前面开始出的空格字符
例子:
sort -t $'\t' -k 1 -u res.txt> res2.txt
以 tab 作为分隔符, 按第一列排序并去重
uniq 参数
需要注意 uniq 需要文本是有序的, 所以一般使用 uniq 的时候是用更早 sort 的管道后面
-c: 显示出现的次数
-d: 仅显示重复出现行;
-u : 仅显示出一次的行列;
说说 sort|uniq 和 sort -u, 一直觉得很奇怪, 两者有什么区别, 功能是一样的. sort -u 是后面加入的, 所以很多人还是使用了 sort|uniq,
目前推荐使用 sort -u, 因为还少了进程间通讯.
五, 实战
处理以下文件内容, 将域名取出并进行计数排序, 如处理:
- http://www.baidu.com/index .HTML http://www.2cto.com/kf/qianduan/CSS/
- http: // www.baidu.com/1.HTML
- http://post.baidu.com/index.html
- http://mp3.baidu.com/index.html
- http://www.baidu.com/3.html
- http://post.baidu.com/2.html
得到如下结果:
- 3 www.baidu.com
- 2 post.baidu.com
- 1 mp3.baidu.com
解法 1:grep -Po '(?<=//)(.*?)(?=/)' test.txt |sort |uniq -c|sort -nr
1. 利用了 Perl, 他支持非贪婪, 2. 利用了正向和反向预查 (正向预查是后面的 (?=)) 3. 利用了 - o 参数只输出匹配的内容
解法 2:awk -F/ '{print $3}' test.txt |sort |uniq -c|sort -nr
指明了分割符号 直接取对应值
解法 3:sed 's/http:\/\/\([^/]*\).*/\1/' test.txt|sort |uniq -c|sort -nr
基本的正则中小括号需要转义, 如果采用 - r 参数即扩展的正则小括号不用转义
解法 4: sed -e 's/http:\/\///' -e 's/\/.*//' | sort | uniq -c | sort -rn
采用了替换, 先替换前面的, 在替换后面的
awk 例子
需要注意 awk 不支持多维数组, 采用了一种变通的方式, 普通的使用没问题, 但是如果需要存的值是一个 map 就不合适了, 如下
文件 1-6 列分别为 deal od sum up lj day , 现在要计算 sum up lj day 的累加和输出
输出也要是 deal od sum up lj day 也就是 sum up lj day 需要是一个 map, 不过 awk 做不到这点
- {
- updealids:{
- od: {day,sum,up,lj}
- }
- }
- awk 'BEGIN{OFS="\t"}{result[$1,$3,"sum"]+=$4;result[$1,$3,"up"]+=$5;result[$1,$3,"lj"]+=$6;result[$1,$3,"day"]=$2}\
- END{for ( i in result) {split(i, a, SUBSEP); print result[i] ,a[1], a[2], a[3] }}' *
参考资料:
AWK 简明教程 https://coolshell.cn/articles/9070.html
SED 简明教程 https://coolshell.cn/articles/9104.html
来源: https://www.cnblogs.com/chenfangzhi/p/11997028.html