【问题标题】:GNU sed cannot match against last newline in a combined stringGNU sed 无法匹配组合字符串中的最后一个换行符
【发布时间】:2014-02-07 14:00:49
【问题描述】:

问题是,当所有传递的字符串合并为一个以匹配换行符时,如果输入有一个尾随换行符,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 本身匹配。如果我正确理解了信息页面,它应该在多行模式下,即当替换具有 Mm 修饰符时。但事实并非如此。还提到了像(实际上是坟墓标记)和\'(直单引号)这样的组合,它们应该在多行模式下匹配缓冲区边界,但它们在我的shell(GNU bash-1.4.45)中不起作用它们有特殊的含义。

【问题讨论】:

  • 至于最后一段尝试:echo -en "aa\nbb\ncc\ndd" |sed 'N;N;N;s/\`/<<</;s/\'\''/>>>/' N.B.在 sed 命令周围使用单引号,必须引用 \'

标签: regex bash sed


【解决方案1】:

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 (without \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 被附加到输出中,因为在最后一行的末尾有一个。

【讨论】:

    猜你喜欢
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-07
    • 2018-10-27
    • 2011-09-25
    • 1970-01-01
    • 2015-12-18
    相关资源
    最近更新 更多