sed命令中的N

背景

想把下面一组的uid和wealth弄到同一行,有什么好的实现方式呢?

1
2
3
4
5
6
7
8
9
10
11
12
uid[1025472]
wealth[33600]
uid[1025472]
wealth[33600]
uid[1031052]
wealth[4800]
uid[1031052]
wealth[4800]
uid[1031697]
wealth[4800]
uid[1031697]
wealth[4800]

答案

1
sed 'N;s/\n//g' w.log

和awk类似,sed语句的基本结构也是 sed ‘模式{动作1;动作2}’ file 的执行方式。不过sed中有两个空间,一个叫pattern space(模式空间),一个叫hold buffer(缓冲区/缓冲空间),sed中的模式匹配的对象只是模式空间,和awk一样,sed也是一行一行的读入文本,不过sed在执行命令前,会将文本的行读到模式空间中,然后执行模式{动作}语句,而在执行命令完后,默认sed还是把模式空间的内容打印出来(通过-n 参数可以把最后默认打印模式空间的动作取消)。

实例1

1
2
3
4
seq 6 | sed -n 'N;P'
1
3
5

[解析]

这个命令就是输出的奇数行,怎么来实现的呢?首先N的作用是读取下一行内容,追加到模式空间里,那sed是逐行执行的,这时候读取1,N把下一行追加到模式空间,那模式空间里的内容就变成了 1\n2 虽然是2行,但是这时候sed把2行看做中间有个换行符的一个整体内容,在模式空间里面。然后继续执行命令P,打印第一行内容。那么1就输出到了屏幕上,接着读取3,N追加4,P输出3,这样一直按这个顺序执行,输出了所有奇数行。这个命令是不是很巧妙?

实例2

1
2
3
seq 5 | sed -n 'N;P'
1
3

[解析]

输出奇数行?5没有输出。是的,这是为什么呢?可以info sed看看N的解释“If there is no more input then ‘sed’ exits without processing any more commands.”,如果没有可以追加的内容,那么sed将不会处理后面的任何命令。当sed读到5的时候,已经没有下一行内容了,那么N将退出,也不再执行后面的P。所以5没有输出。怎么解决呢?

1
2
3
4
seq 5 | sed -n '$!N;P'
1
3
5

[解析]

$!N就是N对最后一行不起作用