问题是,当所有传递的字符串合并为一个以匹配换行符时,如果输入有一个尾随换行符,则sed有一个与之匹配的问题。
一个简单的字符串。
$ echo -en "aa\nbb\ncc\ndd" | hexdump -C
00000000 61 61 0a 62 62 0a 63 63 0a 64 64 |aa.bb.cc.dd|
0000000b
在这种情况下,如果我们需要使用空字符而不是换行符包围最后两段文本,它可以正常工作。
$ echo -en "aa\nbb\ncc\ndd" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00\2\x00\3\x00/; p}' \
| hexdump -C
00000000 61 61 0a 62 62 00 63 63 00 64 64 00 |aa.bb.cc.dd.|
0000000c
但是如果输入有一个尾随换行符,那么在替换正则表达式中附加一个尾随\n
并不会使它匹配。
$ echo -en "aa\nbb\ncc\ndd\n" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)\n$/\1\x00\2\x00\3\x00/; p}' \
| hexdump -C
00000000 61 61 0a 62 62 0a 63 63 0a 64 64 0a |aa.bb.cc.dd.|
0000000c
但是,如果我们没有在regexp中添加尾随换行符,它仍会匹配!
$ echo -en "aa\nbb\ncc\ndd\n" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00\2\x00\3\x00/; p}' \
| hexdump -C
00000000 61 61 0a 62 62 00 63 63 00 64 64 00 0a |aa.bb.cc.dd..|
0000000d
但它似乎只是忽略了输入中的尾随换行符,或$
以某种方式匹配它本身。我在sed FAQ on sourceforge(第5.10节)中发现sed在将它放到模式空间之前对该行的尾部换行进行条纹化处理,甚至在输出中添加了一个尾随换行符,但是,从第二行中可以清楚地看到第三个例子,它没有做任何事情。
所以我正在阅读和阅读并回过头来想到$
在某种程度上与那个尾随\n
本身相匹配。如果我正确理解了信息页面,它应该在多行模式下,即当替换具有M
或m
修饰符时。但事实并非如此。还提到了像\´
(实际上是严重标记)和\'
(直单引号)之类的组合,它们应该在多行模式下匹配缓冲区边界,但它们在我的shell中不起作用(GNU bash-1.4) .45)因为它们有特殊的意义。
答案 0 :(得分:3)
Sed仅在输出时为输出添加一个尾随换行符。在将该行放入模式空间之前有一个切断的换行符。这在信息中有记录页面。点击此处:How sed Works。具体地,
当到达脚本的末尾时,除非使用-n选项,否则模式空间的内容将打印到输出流,如果删除了后续换行,则添加回来
也就是说,如果它在没有找到换行符的情况下读取了文件的结尾,那么它只会将整行放在模式空间中(这里没有任何内容),当输出模式空间时,它就赢了添加一个新行(因为在第一个地方没有删除任何内容)。这很容易证明:
vivek@vivek-laptop:~ $ PS1=' $ '
$ cat > /tmp/file
aa
aa $ sed 's/aa/bb/' /tmp/file
bb
bb $
我在第二行之后按了ctrl-d,所以在我的文件末尾没有终止的新行。
在进行替换时,sed将读取第一个aa\n
,删除\n
,将aa
放置在模式空间中,进行替换(模式空间现在为bb
}),输出模式空间,并添加\n
。因此,输出bb\n
。
当它读取第二行时,它正在查找换行符或文件结尾以知道何时停止读取当前行。它读取aa
(没有\n
),将其放在模式空间中,进行替换并再次输出模式空间。但是这次没有添加\n
,因为在将模式空间添加到模式空间时没有删除它。
解释您的三种情况:
$ echo -en "aa\nbb\ncc\ndd" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00\2\x00\3\x00/; p}'
在这种情况下,模式空间将为aa\nbb\ncc\ndd
。这正确匹配您的正则表达式。此外,没有\n
将附加到输出(因为最后没有)。
$ echo -en "aa\nbb\ncc\ndd\n" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)\n$/\1\x00\2\x00\3\x00/; p}'
在这种情况下,模式空间将为aa\nbb\ncc\ndd
。此与您的正则表达式不匹配,因此不会进行任何替换。输出\n
。
$ echo -en "aa\nbb\ncc\ndd\n" \
| sed -rn '1h; 2,$ H; ${g; s/^(.*)\n([^\n]+)\n([^\n]+)$/\1\x00\2\x00\3\x00/; p}'
在这种情况下,模式空间将为aa\nbb\ncc\ndd
。这符合你的正则表达式。此外,\n
被添加到输出,因为在最后一行的末尾有一个。{/ p>