sed命令和tr命令的坑

2018-07-18  本文已影响55人  今後次

需求

把log中含有某关键字的所有行输出到另外txt中。

解决办法

# cat service.log |sed -n '/AAA/p' > result.txt

对策

tr,sed

tr用法

为了实验,先创建了文本例子。
其中sql输出部分是用换行和tab来调整格式的(也就是用tab来错位)。

[test@CentOS7 tmp]$ cat service.log 
1 AAAAA
2 BBBBB
3 CCCCC  SELECT
    FROM
    WHERE
4 DDDDD UPDATE
    WHERE

5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;
容易看出空格/tab的截图模式

按照使用其他文本编辑器(我常用EmEditor)的习惯,习惯性地想到把[\n\t]替换成[\t]或者[空格],这样sql部分的换行就会被提升至一行了。
实验对象准备好了,开始敲命令实验吧。

[test@CentOS7 tmp]$ $ cat service.log | tr "\n\t" "\t" > result.txt
[test@CentOS7 tmp]$ cat result.txt 
1 AAAAA 2 BBBBB 3 CCCCC  SELECT     FROM        WHERE   4 DDDDD UPDATE  WHERE       5 EEEEE 6 FFFFF 7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;  [test@CentOS7 tmp]$ 

cat出结果中空格和tab看不到,拷贝到文本编辑器里看一下。


result

截图中点代表空格,线代表tab。

我的期望是有换行且第二行开头是tab的行作为对象(普通换行不受影响),[\n\t]作为一个整体替换成[\t]。
但是看输出的结果,是按照顺序一对一地把[\n\t](前字符串)里的元素逐个替换成[\t](后字符串)。

这个用法失败,tr的坑算是知道怎么回事了。

sed的用法

还是继续使用文本编辑器的思路,把[\n\t]作为一个整体替换成[\t]。
实验结果如下:

[test@CentOS7 tmp]$ cat service.log | sed 's/\n\t/\t/g' > result.txt 
[test@CentOS7 tmp]$ cat result.txt 
1 AAAAA
2 BBBBB
3 CCCCC  SELECT
    FROM
    WHERE
4 DDDDD UPDATE
    WHERE

5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;
[test@CentOS7 tmp]$ 

什么变化都没有。
google之后发现,sed命令竟然对\n无效。
理由是sed命令的作用域只是当前行(简介粗体)。如果要跨行的话,需要加一堆别的参数。

使用如下解决方案:

sed ':a;N;$!ba;s/\n/ /g'

这将在一个循环里读取整个文件,在内存中拼成一行,然后将换行符替换成一个空格,输出到屏幕或者重定向到别的文件中。

实验一下

[test@CentOS7 tmp]$ cat service.log | sed ':a;N;$!ba;s/\n\t/\t/g' > result.txt 
[test@CentOS7 tmp]$ cat result.txt 
1 AAAAA
2 BBBBB
3 CCCCC  SELECT    FROM    WHERE
4 DDDDD UPDATE    WHERE

5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;
[test@CentOS7 tmp]$ 

效果出来了。
接下来就是根据关键字提取对应的行了。

好像哪里不对???

上面的方法实际上是把整个文件都记录进内存,当文件很大的时候,这有把内存全都吃掉的危险。

所以,换个方式。
思路:既然知道了 [:a]标记,有循环处理的功能了,不妨试试。

[test@CentOS7 tmp]$ cat service.log  
1 AAAAA
2 BBBBB
3 CCCCC  SELECT
    FROM
    WHERE
4 DDDDD UPDATE
    WHERE

5 EEEEE
5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;
[test@CentOS7 tmp]$ cat service.log | sed ':a;N;s/\n\t/\t/g;ba'
1 AAAAA
2 BBBBB
3 CCCCC  SELECT FROM    WHERE
4 DDDDD UPDATE  WHERE

5 EEEEE
5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;

结果成功!!

内容扩展

关于sed命令中[b]的用法,跟[t]容易弄混。作为参照,我把ta执行的结果也贴出来。这个实际上是失败的例子,不过却能很好帮助理解区别。

[test@CentOS7 tmp]$ cat service.log | sed ':a;N;s/\n\t/\t/g;ta'
1 AAAAA
2 BBBBB
3 CCCCC  SELECT FROM    WHERE
4 DDDDD UPDATE
    WHERE

5 EEEEE
5 EEEEE
6 FFFFF
7 XXXXXX SELECT COUNT(1) FROM TABLE WHERE ;;;;

具体说明,这里省略。详细可以参考以下例子。
http://man.linuxde.net/sed :说明和例子详细齐全。只是有很多参数的描述太难理解。
关于b、t的区别,可以参考下面两个链接。
http://blog.chinaunix.net/uid-639516-id-2692525.html
http://www.bubuko.com/infodetail-1655818.html

上一篇下一篇

猜你喜欢

热点阅读