Linux三剑客awk、grep、sed详解
⼀、前⾔
linux 有很多⼯具可以做⽂本处理,例如:sort, cut, split, join, paste, comm, uniq, column, rev, tac, tr, nl, pr, head, ,学习 linux ⽂本处理的懒惰⽅式(不是最好的⽅法)可能是:只学习grep,sed和awk。使⽤这三个⼯具,你可以解决近 99% linux 系统的⽂本处理问题,⽽不需要记住上⾯不同的命令和参数。:)
⽽且,如果你已经学会并使⽤了三者,你就会知道其中的差异。实际上,这⾥的差异意味着哪个⼯具擅长解决什么样的问题。
⼀种更懒惰的⽅式可能是学习脚本语⾔(python,perl或ruby)并使⽤它进⾏每个⽂本处理。
⼆、概述
awk、grep、sed 是 linux 操作⽂本的三⼤利器,也是必须掌握的 linux 命令之⼀。三者的功能都是处理⽂本,但侧重点各不相同,其中属awk 功能最强⼤,但也最复杂。grep 更适合单纯地查或匹配⽂本,sed 更适合编辑匹配到的⽂本,awk 更适合格式化⽂本,对⽂本进⾏较复杂格式处理。
简单概括:
grep:数据查定位
awk:数据切⽚
sed:数据修改
三、grep = global regular expression print
⽤最简单术语来说,grep(全局正则表达式打印)--命令⽤于查⽂件⾥符合条件的字符串。从⽂件的第⼀⾏开始,grep 将⼀⾏复制到 buffer 中,将其与搜索字符串进⾏⽐较,如果⽐较通过,则将该⾏打印到屏幕上。grep将重复这个过程,直到⽂件搜索所有⾏。
注意这⾥没有进程执⾏ grep 存储⾏、更改⾏或仅搜索部分⾏。
1、⽰例数据⽂件
请将以下数据剪切粘贴到⼀个名为 “sampler.log” 的⽂件中:
boot
book
booze
machine
boots
bungie
bark
aardvark
broken$tuff
robots
2、⼀个简单例⼦
grep 最简单的例⼦是:
grep "boo" sampler.log
在本例中,grep 将遍历⽂件 “sampler.log” 的每⼀⾏,并打印出其中的每⼀⾏包含单词“boo”:
boot
book
booze
boots
但是如果你操作的是⼤型⽂件,就会出现这种情况:如果这些⾏标识了⽂件中的哪⼀⾏,它们是什么,可能对你更有⽤,如果需要在编辑器
中打开⽂件,那么可以更容易地跟踪特定字符串做⼀些改变。这时候可以通过添加 -n 参数来实现:
grep -n "boo" sampler.log
这产⽣了⼀个更有⽤的结果,解释了哪些⾏与搜索字符串匹配:
1:boot
2:book
3:booze
5:boots
另⼀个有趣的参数是 -v,它会打印出相反的结果。换句话说,grep 将打印所有与搜索字符串不匹配的⾏,⽽不是打印与之匹配的⾏。
在下列情况下,grep 将打印不包含字符串 “boo” 的每⼀⾏,并显⽰⾏号,如上⼀个例⼦所⽰
grep -vn "boo" sampler.log
4:machine
6:bungie
7:bark
8:aardvark
9:broken$tuff
10:robots
c 选项告诉 grep 抑制匹配⾏的打印,只显⽰匹配⾏的数量,匹配查询的⾏。
例如,下⾯将打印数字4,因为有4个在 sampler.log 中出现 “boo”。
grep -c "boo" sampler.log
4
l 选项只打印查询中具有与搜索匹配⾏的⽂件的⽂件名字符串。如果你想在多个⽂件中搜索相同的字符串,这将⾮常有⽤。像这样:
grep -l "boo" *
对于搜索⾮代码⽂件,⼀个更有⽤的选项是 -i,忽略⼤⼩写。这个选项将处理在匹配搜索字符串时,⼤⼩写相等。在下⾯的例⼦中,即使搜索字符串是⼤写的,包含“boo”的⾏也会被打印出来。
grep -i "BOO" sampler.log
boot
book
booze
boots
x 选项只精确匹配。换句话说,以下命令搜索没有结果,因为没有⼀⾏只包含"boo"
grep -x "boo" sampler.log
最后,-A 允许你指定额外的上下⽂件⾏,这样就得到了搜索字符串额外⾏,例如
grep -A2 "mach" sampler.log
machine
boots
bungie
grep命令有什么用
3、正则表达式
正则表达式是描述⽂本中复杂模式的⼀种紧凑⽅式。有了 grep 你可以使⽤搜索模式( pattern ) 。其他⼯具使⽤正则表达式 (regexp) 以复杂的⽅式。
⽽ grep 使⽤的普通字符串,实际上⾮常简单正则表达式。如果您使⽤通配符,如 ' * ' 或 ' ? ',⽐如列出⽂件名等,你可以使⽤ grep 使⽤基本的正则表达式进⾏搜索例如搜索⽂件以字母 e 结尾的⾏:
grep "e$" sampler.log
booze
machine
bungie
如果需要更⼴泛的正则表达式命令,则必须使⽤grep -E。例如,正则表达式命令 ? 将匹配1或0次出现之前的字符:
grep -E "boots?" sampler.log
boot
boots
你还可以使⽤ pipe(|) 结合多个搜索,它的意思是 “或者”,所以你可以这样做:
grep -E "boot|boots" sampler.log
boot
boots
4、特殊字符
如果你想搜索的是⼀个特殊字符,该怎么办?如果你想到所有的直线,如果包含美元字符“$”,则不能执⾏ grep “ $ ” a_file,因为 '$' 将被解释为正则表达式,相反,你将得到所有的⾏,其中有任何作为⾏结束,即所有⾏。解决⽅案是 “转义” 符号,所以您将使⽤
grep '\$' sampler.log
broken$tuff
你还可以使⽤ “-F” 选项,它代表“固定字符串”或“快速”,因为它只搜索字符串,⽽不是正则表达式。
5、更多的 regexp 的例⼦
参考:
四、AWK
由 Aho,Weinberger 和 Kernighan 创建的⽂本模式扫描和处理语⾔。 AWK⾮常复杂,所以这不是⼀个完整的指南,但应该给你⼀个知道什么 awk 可以做。它使⽤起来⽐较简单,强烈建议使⽤。
1、AWK 基础知识
awk 程序对输⼊⽂件的每⼀⾏进⾏操作。它可以有⼀个可选的 BEGIN{ } 部分在处理⽂件的任何内容之前执⾏的命令,然后主{ }部分运⾏在⽂件的每⼀⾏中,最后还有⼀个可选的END{ }部分操作将在后⾯执⾏⽂件读取完成:
BEGIN { …. initialization awk commands …}
{ …. awk commands for each line of the file…}
END { …. finalization awk commands …}
对于输⼊⽂件的每⼀⾏,它会查看是否有任何模式匹配指令,在这种情况下它仅在与该模式匹配的⾏上运⾏,否则它在所有⾏上运⾏。这些 'pattern-matching' 命令可以包含与 grep ⼀样的正则表达式。 a
wk 命令可以做⼀些⾮常复杂的数学和字符串操作,awk也⽀持关联阵列。 AWK 将每条线视为由多个字段组成,每个字段由“间隔符”分隔。默认情况下,这是⼀个或多个空格字符,因此⾏:
this is a line of text
包含6个字段。在 awk 中,第⼀个字段称为 2,等等,全部⾏称为 $0。字段分隔符由 awk 内部变量 FS 设置,因此如果您设置 FS= ": "则它将根据 ':' 的位置划分⼀⾏,这对于/etc/passwd 之类的⽂件很有⽤,其他有⽤的内部变量是 NR,即当前记录号(即⾏号) NF是当前⾏中字段的数量。
AWK 可以对任何⽂件进⾏操作,包括 std-in,在这种情况下,它通常与 '|' 命令⼀起使⽤,例如,结合 grep 或其他命令。例如,如果我列出当前⽬录中的所有⽂件
ls -l
总⽤量 140
-rw-r--r-- 1 root root 55121 1⽉  3 17:03 combined_log_format.log
-rw-r--r-- 1 root root 80644 1⽉  3 17:03 combined_log_format_w_resp_time.log
-rw-r--r-- 1 root root    71 1⽉  3 17:55 sampler.log
我可以看到⽂件⼤⼩报告为3 列数据。如果我想知道它们的总⼤⼩,这个⽬录中的⽂件我可以做:
ls -l | awk 'BEGIN {sum=0} {sum=sum+$5} END {print sum}'
135836
请注意,'print sum' 打印变量 sum 的值,因此如果 sum = 2 则 'print sum' 给出输出 '2' ⽽ 'print $ sum' 将打印 '1' ,因为第⼆个字段包含值 '1'。
因此,会很简单编写⼀个可以计算平均值的和⼀列数字的标准偏差的 awk 命令 - 在主要内部积累 'sum_x' 和 'sum_x2' 部分,然后使⽤标准公式计算 END 部分的平均值和标准偏差。 AWK ⽀持('for' 和 'while')循环和分⽀(使⽤ 'if ')。
所以,如果你想修剪⼀个⽂件并且只在每个第 3 ⾏操作,你可以这样做:
ls -l | awk '{for (i=1;i<3;i++) {getline}; print NR,$0}'
3 -rw-r--r-- 1 root root 8064
4 1⽉  3 17:03 combined_log_format_w_resp_time.log
4 -rw-r--r-- 1 root root    71 1⽉  3 17:5
5 sampler.log
for 循环使⽤ “getline” 命令遍历⽂件,并且每隔3次才打印⼀⾏。注意,由于⽂件的⾏数是4,不能被3整除,所以最后⼀个命令提前完成,所以最后的 “print $0” 命令打印第4⾏,你可以看到我们也打印了⾏,使⽤ NR 变量输出⾏号。
2、AWK 模式匹配
AWK 是⼀种⾯向⾏的语⾔。⾸先是模式,然后是动作。操作语句⽤{ and }括起来。模式可能缺失,或者动作可能缺失,但是,当然不是都。如果缺少模式,则对每个输⼊记录执⾏操作。⼀个丢失的动作将打印整个记录。
AWK 模式包括正则表达式(使⽤与“grep -E”相同的语法)和使⽤的组合特殊符号 “&&” 表⽰“逻辑AND ”,“||”表⽰“逻辑或”,“!” 的意思是“逻辑不”。你也可以做关系模式、模式组、范围等。
3、AWK 控制语句
if (condition) statement [ else statement ]
while (condition) statement
do statement while (condition)
for (expr1; expr2; expr3) statement
for (var in array) statement
break
continue
exit [ expression ]
4、AWK 输⼊/输出语句
注意:printf 命令允许你使⽤类似 C 的语法更密切地指定输出格式例如,你可以指定给定宽度的整数,浮点数或字符串等。
5、AWK 数学函数
6、AWK 字符串函数
7、AWK 命令⾏和⽤法
你可以根据需要多次使⽤ ' -v ' 标志将变量传递给 awk 程序,例如
awk -v skip=3 '{for (i=1;i<skip;i++) {getline}; print $0}' sampler.log
booze
bungie
broken$tuff
你还可以使⽤编辑器编写 awk 程序,然后将其另存为脚本⽂件,例如:
$ cat awk_strip
#!/usr/bin/awk -f
#only print out every 3rd line of input file
BEGIN {skip=3}
{for (i=1;i<skip;i++)
{getline};
print $0}
然后可以将其⽤作新的附加命令
chmod u+x awk_strip
./awk_strip sampler.dat
五、sed = stream editor
sed 对输⼊流(⽂件或来⾃管道的输⼊)执⾏基本⽂本转换单通过流,所以效率很⾼。但是, sed 能够管道过滤⽂本,特别区别于其他类型的编辑器。
1、sed 基础
sed 可以在命令⾏或 shel l脚本中使⽤,以⾮交互⽅式编辑⽂件。也许最有⽤的功能是对⼀个字符串进⾏ “搜索和替换” 到另⼀个字符串。
您可以将 sed 命令嵌⼊到使⽤ '-e' 选项调⽤ sed 的命令⾏中,或者将它们放在⼀个单独的⽂件中 'sed.in' 并使⽤ '-f sed.in' 选项调⽤ sed。后⼀种选择是如果 sed 命令很复杂并涉及⼤量regexp,则最常⽤!例如:
sed -e 's/input/output/' sampler.log
boot
book
booze
machine
boots
bungie
bark
aardvark
broken$tuff
robots
将从 sampler.log 回显到标准输出的每⼀⾏,改变每⼀⾏的 'input' 排成 'output'。注意 sed 是⾯向⾏的,所以如果你想改变每⼀⾏的每⼀个事件,那么你需要让它成为⼀个 '贪婪' 的搜索和替换,如下所⽰:
sed -e 's/input/output/g' sampler.log
boot
book
booze
machine
boots
bungie
bark
aardvark
broken$tuff
robots
/.../ 中的表达式可以是⽂字字符串或正则表达式。注意默认情况下,输出将写⼊ stdout。你可以将其重定向到新⽂件,或者如果你愿意编辑现有⽂件,你应该使⽤ '-i' 标志:
sed -e 's/input/output/' sampler.log  > new_file