Linux上文本处理三剑客
grep, egrep, fgrep:文本过滤工具(模式:pattern)工具;
grep:基本正则表达式,-E,-F
egrep:扩展正则表达式, -G,-F
fgrep:不支持正则表达式,
sed:stream editor, 流编辑器;文本编辑工具;
awk:Linux上的实现为gawk,文本报告生成器(格式化文本)名字来源于作者的名字;
Regular Expression ,REGEXP 正则表达式
正则表达式用来在文件中匹配符合条件的字符串,正则是贪婪匹配(凡是有的一行都列出)。grep,awk,sed等命令可以支持正则表达式。字符叫元字符。通配符用来在系统中匹配符合条件的文件名,通配符是完全匹配(只列有的)。ls find cp不支持正则表达式。
在有.*
的匹配中似乎遵循着最近匹配的做法。
\ 转义符,后面的字符被grep使用。代表取消特殊符号的含义取消。(使符号的图书含义取消,后面的符号变为纯字符)
基本正则BRE
字符匹配
. 匹配除换行外的任意一个字符.
[]中括号中的一个字符. []匹配括号中任意一个字符. 例如[abcd][12345],任意[a-d][1-4]中的任意.
[^] 括号内除外.
[:digit:]数字、[:lower:]小写、[:upper:]大写、[:alpha:]字母、[:alnum:]字母数字、[:punct:]标点、[:space:]空格 [][xdigit]16进制
man 7 glod
次数匹配
用于要指定其出现的次数的字符后面,用来限制前面的字符要出现的次数。默认工作在贪婪模式下。
正则也会统计空格数,但是一个tab和一个space都被认为一个空格。
* 前一个字符匹配0次或任意次; .*表任意长度的任意字符 。
\?任意字符重复1次,或0次, 即其前面的字符是可有可无的;
\+:匹配其前面的字符1次或多次,即其前面的字符要出现至少1次。因为+有特殊意思,所以要转义;
\{0,m\}前面的字符至多m次。
\{n,m\} 前面的字符至少出现n次,最多出现m次。例如[a-z]\{6,8\}匹配6-8位的小写字母。
\{n\} 代表前面的字符恰好出现几次;例如[0-9]\{4\}匹配四位数字,[1][3-8][0-9]\{9\}匹配手机号码。
\{n,\}前面的数字出现不少于n次;[0-9]\{2,\}表示两位以上的数字。
锚定位置
标定出现的位置
^匹配行首,^hello 任意一hello开头的行。
$匹配行尾,hello$ 任意一hello结尾的行。
^PATTERN$ 用于PATTERN来匹配整行;
^$ 匹配空白行;
^[[:space:]]*$ 空行或包含空白字符的行;
由$
非特殊字符$
组成的连续字符(字符串)都称为单词;
\< 或 \b 后面的字符必须做为单词首部出现,\<PATTERN,\bPATTERN;
\> 或 \b 后面出现的任意字符必须以单词的尾部出现.PATTERN\>,\bPATTERN;
\<root\> \broot\b 以root作为整个单词出现的,独立出现。
\<[0-9]\{2,\}\>表示两位的数字
内容分组及引用:
前面匹配到的后面也原封不动的匹配。相当于匹配两个一模一样的。也叫后向引用。
后向引用:引用前面的分组括号中的模式所匹配到的字符;
\(PATTERN\) 将一个或多个字符捆绑在一起,当作一个整体进行处理;
\(ab\)* ab出现零次或任意次
Note:分组括号中的模式匹配 到的内容会被正则表达式引擎自动记录于内部的变量中,这些变量为:
\1:模式从左侧起,第一个左括号以及与之匹配的右括号之间的模式所匹配到的字符;
\2:模式从左侧起,第二个左括号以及与之匹配的右括号之间的模式所匹配到的字符;
\n:模式中第n个左括号以及与之匹配的右括号之间的模式所匹配的字符串;
...
grep "\(l..e\).*\1" lovers.txt
出现l…e,中间任意填充,后面又出现l…e且前后的l…e相同。
a-zA-Z
匹配字母
's/^[0-9]*$'
匹配文件名
正则表达式在处理开头有空格和空白行时的做法是不同的最好拿不同的式子匹配。/^[[:space:]]\+
空白开头的行。
/^$/
空行。 可以反映在sed中。
扩展正则表达式 :egrep ERE
grep -E 或 egrep [OPTIONS] PATTERN [FILE…]
扩展正则表达式的元字符:
字符匹配:
. 任意单个字符
[] 指定范围内的任意单个字符
[^] 指定范围外的任意单个字符
次数匹配:
*:任意次,0,1或多次;
?:0次或1次,其前的字符是可有可无的;
+:其前字符至少1次;
{m}:其前的字符m次;
{m,n}:至少m次,至多n次;
{0,n}
{m,}
位置锚定
^:行首锚定;
$:行尾锚定;
\<, \b:词首锚定;
\>, \b:词尾锚定;
分组及引用:
(PATTERN):分组;括号内的模式匹配到的字符会被记录于正则表达式引擎的内部变量中;
提供后向引用:\1, \2, ...
####或者:
a|b:a或者b;
C|cat:C或cat,整个左侧或右侧
(c|C)at:cat或Cat
a|bc:匹配a或bc ,或者指整个左边或右边
'\<([0-9]|[1-9][1-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' 匹配0-255
egrep -o "[^/]*$"
取绝对路径中的基名,这个列出的没/。 "[^/]+/?$"
列出的目录有./
fgrep–fast grep
不支持正则表达式,不需要正则时效果好。
$(先执行命令)
grep: Global search REgular expression and Print out the line.
作用:文本搜索工具,根据用户指定的“模式(过滤条件)”对目标文本逐行进行匹配检查;打印匹配到的行,默认支持的是基本正则表达式;
模式:由正则表达式的元字符及文本字符所编写出的过滤条件,需要加引号;
grep [options] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
--color 把搜到的关键字颜色表示;
--color==auto 搜索出的关键字用颜色表示高亮显示;
-i ignorecase 忽略大小写;
-v,--invart-match 显示没有被模式匹配的行;
-o 只显示模式匹配的字符串本身;
-q ,--quiet,--silent:静默模式,即不输出任何信息;
-d 可查目录下的文件名,默认查的是文本。
-n 输出行号
-E , --extended-regexp 支持使用扩展正则表达式元字符(grep使用基本正则表达式 );
-F 不使用正则表达式。
-G 使用基本正则。
-P --perl-regexp 支持pcre ,perl的正则。
-e 多模式机制;
-f FILE,--filefile:每行包含了一个pattern的文本文件,即gerp script;
-A # 当grub匹配到,显示后#行
-B # 同上显示前面的#行
-C # 显示前后#行
###sed(stream editor流编辑器)
SED是逐行(行编辑器,记住)读取至内存,再次处理,处理后将模式空间打印至屏幕,默认不对原文件处理。这个内存空间也叫模式空间。可以进行模式匹配。匹配到内容可再次放入保持空间,进行编辑。有没有匹配到默认都会打印至屏幕,除非对其操作。操作后也会打印到屏幕上。
sed是一种几乎包括在 UNIX平台的轻量级编辑器。sed主要是用来将数据进行选取。替换。删除。新增的命令。
vim只能修改文档,sed可以从管道里接受数据更改结果。
sed [OPTION]... 'script' [input-file] ... [选项] '[编辑命令]' 文件名
选项:
-n 禁止默认显示行为,只保留编辑后的行为,在不使用d命令的情况下一般都要使用。不输出模式空间中的内容至屏幕;
-e script, --expression=script:允许对输出数据应用多条sed命令编辑。如果写下一行用\分割下一行也-e,如果写在一行上直接-e编辑。
-i 用sed的修改结果直接修改读取数据的文件。而不是由屏幕输出。(输出数据和文件数据都改)
-r --regexp-extended 表示使用扩展正则表达式。
-f PATH/TO/SED_SCRIPT_FILE 通过读取脚本,每行执行一个sed编辑命令,;
script: '地址定界或PATTERN 编辑命令'
地址定界:
(1) 空地址:对全文进行处理;
(2) 单地址:
#:指定行;
/pattern/:被此模式所匹配到的每一行;
(3) 地址范围
#,#:
#,+#:
#,/pat1/
/pat1/,/pat2/ //从第一次被匹配到的行开始第二次匹配到的行结束。
$:最后一行;
(4) 步进:~
1~2:所有奇数行; 2~2:所有偶数行;
编辑命令:
a \string 在文本后追加string。\n 可用于换行,实现多行追加;
c \string 行替换 ,把匹配到的行替换为此处指定的string,换行每行后加“\n”;
i \string 在行前插入。一行或多行,除最后与一行,每行后加 \n;
d 删除模式空间中内容。整行删除。
p 打印,显示模式空间中的内容,输出指定行。
w /PATH/TO/SOMEFILE: 保存模式空间匹配到的行到指定文件。
r /PATH/TO/FILE 将指定的文件的内容内的添加至模式匹配的行后面,文件合并。 read
=:为模式匹配到的行打印行号;
!:条件取反:地址定界!编辑命令;
s /pattern/string/字符串查找替换,分隔符不用要操作的分隔符,其他符号当作分隔符也行,格式为“几行s/旧字符串/新字符串/g”
替换标记:
g:全局替换;
i: 忽略字符大小写;
w:/PATH/TO/SOMEFILE:将替换成功的结果保存至指定文件中;
p: 显示替换成功的行;
高级编辑命令:
h:把模式空间中的内容覆盖至保持空间中;
H:把模式空间中的内容追加至保持空间中;
g:把保持空间中的内容覆盖至模式空间中;
G:把保持空间中的内容追加至模式空间中;
x:把模式空间中的内容与保持空间中的内容互换;
n:覆盖读取匹配到的行的下一行至模式空间中;
N:追加读取匹配到的行的下一行至模式空间中;
d :删除模式空间中的行;
D:删除多行模式空间中的所有行;
eg
sed '2a \hello' student.txt //在第二行之后追加hello
sed '2i hello \ world' student.txt //在第二行之前插2行数据 hello(第一行) world(第二行)
sed '4s/99/55/g’ student.txt //把4行的99换成55
sed '/oot/d' /etc/fstab //删除oot在/etc/fstab
sed '/^\//p' /etc/fstab 显示/开始的行
sed '/^\//a \abc' /etc/fstab /开头的行后追加abc
sed '/oot/w /tmp/oot.txt' /etc/fstab 有oot的一行保存至/tmp/oot.txt目录下,处理文件/etc/fatab
sed 's#l\(..e\)#L\1#g' sed.txt 把l...e中的l换为大写。
sed -n 'n;p' /etc/fstab 显示偶数行,;
sed '1!G;h;$!d' FILE 逆序显示文件内容
sed ‘!d’FILE 显示最后一行
sed ‘$!N;$!d’FILE 最后两行
sed ‘/^$/d;G’ 删除原有的所有空白行,而后为所有空白行后添加一个空白行;
sed ‘n;d’FILE; 显示奇数行,按行读取的。
sed ‘G’FILE 在原有行后添加空白行;
echo“/etc/fstab”| sed '@[^/]\+/\?$@@' 取出基名
awk
awk介绍
AWK: Aho, Weinberger, Kernighan --> New AWK, NAWK(更新版) 三个作者的合称。出现在UNIX上。
gawk - pattern scanning and processing language ,模式扫描及处理语言。GNU awk, gawk awk是gawk的链接。Linux端使用。识别列,列编辑器,数据处理工具,一次读取一行文件,处理每一行的字段内数据,而默认的的字段分隔符为空格或[tab]键。复杂到可形成自己的体系,还可以编程调用,脚本解释器。
awk用法
awk '{动作1}条件类型2{动作2}...' filename
awk -F CHAR '/pattern/{print $1, $2, ...}' FILE...
-F:指明输入分隔符
$1,$2,...:处理那些字段;
条件(pattern) 使用关系表达<>=
动作(Active) 格式化输出 ,流程控制语句
last -n 5 | awk '{print $1 "\t" $3}' // 输出第二列,制表符,第3列,制表符
用法:
gawk [options] ‘program’ FILE …输入一次读取一行,自动分割,赋予内建变量,加工处理,输出。
program: PATTERN{ACTION STATEMENTS} //此处的模式有点地址定界的意思
语句之间用分号分隔 ,用 print, printf 输出
基本选项:
-F:指明输入时用到的字段分隔符 ,分割符可直接写也可加[],也可“”,也可‘’;
-v var=value: 用来实现自定义变量;
1.print
print item1, item2, ...
要点:
1. 逗号分隔,输出空格隔开。如果不加逗号,输出会连在一起;
2. 输出的各item可以字符串,也可以是数值,也可以是当前记录的字段、变量或awk的表达式,变量替换的时候不应该被引号引起来;
3. 如省略item,相当于print $0(打印整行);
2.变量类型(引用变量不加$).
2.1 内建变量
$0 表示整行,$#表示第#个片段。引用以下的变量不需要加$.
FS:input field seperator 默认为空白字符,输入分隔符;
OFS:output field seperator 默认为空白字符,输出分隔符;
RS:input record seperator 默认换行符,输入时的换行符,少用;
ORS:output record seperator 默认换行符,输出时的换行符,少用;
awk -v FS=':' '{print $1}' /etc/passwd //指明输入字段分割符 相等于 -F :
awk -F ':' -v OFS= ':' {print $1,$2} /etc/passwd //指明输入输出的分隔符
awk -v RS=' ' '{print}' /etc/passwd //指明输入的换行符是空格,原来的换行符被替换,输出passwd文件时空格作为换行符
awk -v RS=' ' -v ORS='#' {print $1 ,$2} /et/cpasswd //换行当换行,空格也当换行,但被换为#很诡异。
NF:number of field //字段数量.
NR:number of record //行数,输入多个文件的时候,显示的行数是两个文件连起来以后的。
FNR //各文件单独计数,和NR相反。
FILENAME //当前文件名。每处理一行都显示一次,前面加BEGIN显示一次;
ARGC //命令行参数的个数;
ARGV //内建的数组,保存的是命令行本身给定的各参数;
{print NF}, {print $NF} //前者是打印字段数量,后者相当于打印最后一个字段。
{print NR} //打印行数;
awk 'BEGIN{print ARGV[#] }' /etc/fstab /etc/issue //会显示 awk(命令本身作为一个) /etc/passwd /etc/issue 三个字段。
2.1自定义变量
(1) -v var=value 变量名区分字符大小写;
awk -v test=‘hello gawk’ ‘{print test}’ /etc/fstab //定义变量,读取fstab文件,但是读了文件打印的是test,每读一行打印一次。
awk -v test=’hello gawk‘ {print test} //比上面的简单,但是只打印一次。
awk 'BEGIN{test="hello";print test}' //效果同上。不写BEGIN不执行。
(2) 在program中直接定义
3.printf命令
printf FORMAT, item1, item2, ... //格式化输出
1 .FORMAT必须给出;
2. 不会自动换行,需要显式给出换行控制符,\n;
3. FORMAT中需要分别为后面的每个item指定一个格式化符号;
格式符:
%c: 显示字符的ASCII码;
%d, %i: 显示十进制整数;
%e, %E: 科学计数法数值显示;
%f:显示为浮点数;
%g, %G:以科学计数法或浮点形式显示数值;
%s:显示字符串;
%u:无符号整数;
%%: 显示%自身;
awk -F: '{printf "Username: %s,UID: %d\n",$1,$3 }' /etc/passwd
awk -F: '{printf "username:%s \n,$1"}' /etc/passwd 控制输出
规定显示形式
修饰符,控制格式符的显示机制:
#[.#]
:第一个数字控制显示的宽度;第二个#表示小数点后的精度;
%3.1f
-: 左对齐,默认右对齐
+:显示数值的符号(正负)
4.操作符
算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y;
-x; 负数
+x: 转换为数值;
字符串操作符:没有符号的操作符,字符串连接
赋值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比较操作符:
>, >=, <, <=, !=, ==
(注意见下:模式)
模式匹配符:
~:是否匹配 ( 注意见下:模式)
!~:是否不匹配
逻辑操作符:
&&
||
!
函数调用:
function_name(argu1, argu2, ...)
条件表达式:
selector?if-true-expression:if-false-expression
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd //第三字段是否大于1000.输出控制。
- PATTERN,类似于定界符
- empty:空模式,匹配每一行;
- /regular expression/:仅处理能够被此处的模式匹配到的行;取反!/pattern/ ,匹配支持正则。
- relational expression: 关系表达式;结果有“真”有“假”;结果为“真”才会被处理;
真:结果为非0值,非空字符串;
第三字段<1000的打印第一字段和第三字段。
awk -F: '$3<1000{print $1,$3}' /etc/passwd
最后一个字段是否匹配bash,是则打印。(显示用户shell是bash结尾的。)模式需要被//圈起来
awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd //模式匹配
awk -F : '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd //在等于的时候应该加双引号。
注意:在匹配模式的时候需要加双斜线不应该有双引号,在精确匹配的时候需要加双引号内部也不应该有斜线。
- line ranges:行范围。
startline,endline:/pat1/,/pat2/
注意: 不支持直接给出数字的格式。
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd` 行数的标记。
5. BEGIN/END模式
BEGIN{}: 仅在开始处理文件中的文本之前执行一次;
END{}:仅在文本处理完成之后执行一次;
如果去掉BEGIN写在一起,每行执行一次。
6.常用的action
- Expressions 表达式
- Control statements:if, while等,控制语句;
- Compound statements:组合语句,组合起来;
- input statements 输入语句
- output statements 输出语句
7.控制语句
if(condition) {statments} //单分支
if(condition) {statments} else {statements} //双分支
while(conditon) {statments}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break
continue
delete array[index] //删除数组的元素
delete array
exit //退出
{ statements } //多个语句写成组合语句的时候需要花括号括起来。
注意:AWK的循环主要是为了完成字段内的遍历 ,本身可以遍历文本。
7.1 if-else
使用场景:对awk取得的整行或某个字段做条件判断;
语法:if(condition) statement [else statement]
awk -F: '{if($3>=1000) print $1 $3}' /etc/passwd //单分支 ID大于等于1000 显示
awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd //ID界定普通用户和系统用户。
awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd //当用户的默认shell是bash。
awk '{if(NF>5) print $0}' /etc/fstab //某行的字段数大于5个,才显示。
df -h | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>=20) print $1}' // 统计df -h 的信息 。输出 占用超过20%的分区。
7.2 while循环
语法:while(condition) statement
条件“真”,进入循环;条件“假”,退出循环;
使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用;
内建函数length()
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg //每一行都做格式化处理显示,显示linux16开头的行 打印字段 字段长度是多少
awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
7.3do-while循环
语法:do statement while(condition)
意义:至少执行一次循环体
7.4for循环
语法:for(expr1;expr2;expr3) statement
for(variable assignment(初始化);condition(判断);iteration process(迭代)) {for-body}
awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg //linux16开头的行显示每个字段,有几个字符
for特殊用法:
能够遍历数组中的元素; 语法:for(var in array) {for-body} (详见8.array)
7.5switch语句
用来做字符串比较判断。
语法:switch(expression) {case VALUE1 or /REGEXP/: statement; case VALUE2 or /REGEXP2/: statement; ...; default: statement}
7.6 break和continue
break [n]
;`continue’;
7.7 next
提前结束对本行的处理而直接进入下一行;
awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd //显示与用户ID为偶数的行
8.array
关联数组:array[index-expression]
index-expression:
- 可使用任意字符串;字符串要使用双引号,值也要加双引号;
- 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”,当数值使用时会当成0用;
若要判断数组中是否存在某元素,要使用"index in array"格式进行;
weekdays[“mon”]=“Monday” 引用数元素的时候不需要加 i]`
awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}' // 开始的i 表示的是数组元素的索引,遍历次序未必从前到后。
注意:var会遍历array的每个索引;
state["LISTEN"]++
state["ESTABLISHED"]++ 统计状态
netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}' //统计tcp的各种状态
awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log //统计httpd的访问日志,查看哪个用户访问出现了几次。
awk统计遍历一个文件特别有用。特别是搜索状态。
练习1:统计/etc/fstab文件中每个文件系统类型出现的次数;
awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab //统计uuid开头的行的第四个字段,然后输出。
练习2:统计指定文件中每个单词出现的次数;
awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab
统计的结果输出写在了END里。
9.函数
9.1 内置函数
数值处理:
三角函数,平方。
rand():返回0和1之间一个随机数;
字符串处理:
length([s]):返回指定字符串的长度;
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容;
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容,不常用;
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组中,不常用;
netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
9.2 自定义函数
参考:《sed和awk》