awk与sed、grep一样都是为了加工数据流而做成的文本加工过滤器命令。awk会事先把输入的数据根据字段单位进行分割。在没有指定分割单位的情况下,以输入数据中的空格或Tab为分隔符。与sed相比,它以更接近编程语言的文法进行处理,还包括了通过正则表达式进行的字符串操作、简单的数学函数功能等。
sed的基本形式
sed 启动选项
'地址
命令
/查找字符串/替换字符串/标志
' 输入.txt > 输出.txt
启动选项
-e '单行脚本'; -f 脚本文件; -g 整个文章作为对象(global)
-i 将结果覆盖到源输入文件中,可以指定后缀名生成备份文件,如-i.bak
地址
举例 | 含义 |
---|---|
(空,未指定) | 所有行 |
2 | 第2行 |
11,$ | 11~最后一行 |
4,10! | 4~10行以外的行 |
/^[0-9]/ |
所有以数字开头的行 |
2,/END$/ |
从第2行开始到以END结尾的行 |
命令
可以使用;分割组合使用多个命令,按顺序执行。
s
字符串替换;y
字符替换;d
删除;
p
直接输出;w
文件输出;n
数据输入
双直引号与单直引号
为了防止其中的字符作为shell的特殊字符处理。
sed命令操作
s命令替换字符串
cat /etc/passwd | sed "s!/bin/bash$!/bin/zsh!g"
表示将行末以/bin/bash结尾的行中的/bin/bash替换为/bin/zsh其中感叹号!为分隔符,也可以用/、\、#、甚至任意的英文字母,只要不与要替换的字符串发生混淆即可。标志g表示将整个文档作为替换范围,而不是仅替换第一次出现的位置。
sed -e 's!//!#!' hello.c #将//替换为#
sed -e 's/var/+&+/g' a.txt #将var替换为+var+
sed -e 's/.*/result: &/gw output.txt' a.txt
&表示对前边待替换字符串中正则表达式匹配的字符串的引用。
w 指定输出文件名,将替换后的结果写到文件。
y命令替换字符
sed -e 'y/abc/xyz' a.txt #同时进行a->x, b->y, c->z的替换
d命令删除行
sed -e '1,5d' a.txt #删除前5行后输出剩余内容
sed -e 'd' a.txt #删除全文,输出为空,输入文件并不改变
p命令输出
sed -n -e '2,$p' a.txt
sed -n -e '/^aaa/,/eee$/p' a.txt
#以aaa开头的行到以eee结尾的行
脚本文件中命令的组合
对于一个地址可以执行{多个命令}
2,6{
命令1
命令2
....
}
脚本文件结构-标签、分支、循环
: 标签
命令1,2,3...
/模式/b 标签
命令x
示例-删除所有换行符合并字符串
:loop #循环执行的开始位置
N #字符串合并
$!b loop #不是最后一行时跳转
s/\n//g #删除换行符
awk
基本用法
命令基本用法:awk [-F域分隔符] '{pattern + action}' {filenames}
默认的域分隔符是空格,可以指定'\t','\n'等分隔符。其中 pattern 表示 awk 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。
通常,awk 是以文件的一行为处理单位的。awk 每接收文件的一行,然后执行相应的命令,来处理文本。
待处理的文件可以是多个,后面有处理多个文件的例子。
支持awk '/正则表达式/' testfile 筛选匹配的行,支持sub,match,split等正则表达式函数。
正则表达式匹配查找(match使用)
$ awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}'
ok
awk内置变量
变量 | 解释 |
---|---|
$n |
当前记录的第n个字段,n为1表示第一个字段 |
$0 |
执行过程中当前行的文本内容 |
ARGC | 命令行参数的数目 |
ARGV | 包含命令行参数的数组 |
ARGIND | 命令行中当前参数的位置(从0开始算) |
CONVFMT | 数字转换格式(默认值为%.6g) |
ENVIRON | 环境变量关联数组 |
ERRNO | 最后一个系统错误的描述 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔) |
FILENAME | 当前输入文件的名字 |
FS | 字段分隔符(默认是任何空格) |
FNR | 当前行在正在处理的文件中的行号 |
NR | 在执行过程中对应于当前的行号(已处理的所有文件的记录数) |
NF | 表示字段数,在执行过程中对应于当前的字段数 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出字段分隔符(默认值是一个空格) |
ORS | 输出记录分隔符(默认值是一个换行符) |
RS | 记录分隔符(默认是一个换行符) |
RSTART | 由match函数所匹配的字符串的第一个位置 |
RLENGTH | 由match函数所匹配的字符串的长度 |
SUBSEP | 数组下标分隔符(默认值是34) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
print $NF
打印一行中最后一个字段; $(NF-1)
代表倒数第二个字段。
awk内置函数
awk
提供了一些内置函数,方便对原始数据的处理。如函数toupper($1)
用于将字符串$1转为大写。
-
length()
:返回字符串长度。 -
substr()
:返回子字符串。 -
sin()
:正弦。 -
cos()
:余弦。 -
sqrt()
:平方根。 -
rand()
:随机数。
示例1-脚本
可以编写awk脚本,然后传给awk进行解释执行:awk -f awk-script-file input-file(s)
awk统计各个登录shell的用户数:
$ awk '
BEGIN{ FS=":" } #修改字段分隔符为:
{ shells[$NF]++;} #NF保存的是输入行的字段数,$NF可以访问最后一个字段的值
END{
for(sh_i in shells)
print sh_i ": " shells[sh_i];#输出类似/bin/bash: 3
}' /etc/passwd
示例2-日志解析
输出特定行
sed -e '100,200p' 文件名统计行数
awk '{count++} END {print count}' 文件名统计目录下文件的总大小
ls -l | awk '{size+=$5} END {print size}'-
在控制台中高亮显示一些信息
sed -e 's/\(Failed password\)/\x1b[1;36;44m\1\x1b[0m/
' /var/log/secure
其中\x1b
是ESC
这一字符的16进制表示,1;36;44表示文字风格;文字颜色;背景色
,用[x;y;zm表示这个组合的绑定,\x1b[0m表示文字修饰结束。中间的\1
引用前面匹配的\(内容\)
可选高亮设置值 文字风格 0 重置风格 1 粗体 4 下划线 5 点线 7 颜色反转 8 隐藏 值(文字颜色/背景色) 文字颜色/背景色 30/40 黑 31/41 红 32/42 绿 33/43 黄 34/44 蓝 35/45 红紫 36/46 蓝绿 37/47 白 -
显示出现频率较高的top 10个字符串,如访问源IP
awk '
{print $1}
' access_log | sort | uniq -c | sort -nr | head -10注意uniq的去重统计是按照相邻行计算的,只走一遍,所以需要在uniq -c之前先进行sort按字符顺序进行排序。
-
条件筛选-根据URL请求计算疑似非法访问的日志数量
awk '
$9 !~ /200|304/{print $9,$7}
' access_log |sort| uniq -c | sort -nr$9 !~ /模式/
表示筛选掉$9中满足该模式的字符串
示例3-同时处理两个文件
awk 'pattern' file1 file2
在awk里,NR和FNR的含义相近,唯一的区别就是作用范围,NR是所有读取的行信息计数,而FNR是正在读取文件的行信息技术,FNR在文件切换时会从0重新开始计数,所以在awk语句中:
- NR==FNR在判断是不是在读file1;
- NR>FNR则判断是不是在读file2.
awk求两个文件的交集(有相同字段的行)
awk -F'\t' 'NR==FNR{cat[$1]=1}NR>FNR{if($1 in cat){print $0}else{}}' file1 file2
代码的含义是首先处理file1时NR==FNR
,执行cat[$1]=1
,表示创建一个cat词典,当处理file2时,判断file2中的行是否在cat字典中,并进行print $0
(一整行)
awk按照奇数行和偶数行进行处理
使用NR或FNR进行判断,awk '{if (NR%2==1) print $0}' file
输出第一列和第7列相同内容的行
awk -F'\t' '{ if($1==$7){print $0} }' in.txt > out.txt
输出第一列和第7列除去空格后相同内容的行
需要对两列使用gsub函数去除空格然后比较。注意两点:
- 空格分半角' '和全角' ',两者不等同。
-
print ori
不要写成print $ori
, 后者等同于print $0
, 由于gsub改变了$1
和$7
的值,所以$0
的结果是被去除了空格。awk -F'\t' '{ ori=$0; gsub(/ /, "", $1);gsub(/ /, "", $7);if($1==$7){print ori} }' ./in
管道-命令结合
tail -n +2 file | awk '{print $1*$2}'
#表示输出file中从第二行开始到结束的行,跳过首行的描述信息或注释等
tr -d ',-' < file | sed 's/^.../&-'
#将file中的逗号与连字符删掉后在每行前三个字符与后边字符之间加上连字符
echo {1..10} | tr ' ' '\n' | awk '{print $1*2}' | xargs
#xargs将多行的数据转成了单行,输出:2 4 6 8 10 12 14 16 18 20
执行单个awk脚本与用管道进行多个awk串联的比较
使用管道将多个命令串起来将整个任务分解开来无疑更容易理解处理思路,并且利用多CPU并行工作还可提高效率。
如下操作,对数字进行替换,两种方式的执行时间比较:
time seq 1 10000000 | sed -e 's/1/one/g' -e 's/0/zero/g' > /dev/null
V.S.
time seq 1 10000000 | sed 's/1/one/g' | sed 's/0/zero/g' > /dev/null
gawk进行正则表达式字符串提取
gawk 是 AWK 的 GNU 版本。gawk可能没有预装在系统中,需要额外安装。
用法:gawk 'match($0, pattern, ary) {print ary[1]}'
例子:echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}'
其中,a[1]表示括号中匹配的结果.
将a[1]替换成ary['${2:-'0'}']
可以匹配整个字符串
输出: cd.
可以写成一个shell函数,方便调用:
function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }
使用:echo "abcdef" | regex 'b.*f'