sed:匹配“除换行符之外的任何字符”的便携式解决方案

sed:匹配“除换行符之外的任何字符”的便携式解决方案

我可以通过以下方式匹配换行符\n

echo "one
two" | sed 'N;s/\n/_/g'

在 GNU 中sed,我可以用来[^\n]匹配除换行符之外的任何字符:

echo "one
two" | sed 'N;s/[^\n]/_/g'

这非常方便,但它违反了 POSIX。其他sed版本正确回答__n______

与制表符相同,但我可以通过使用实际的制表符(前面是 ctrl-v)来解决。但这不适用于换行符:

echo "one
two" | sed 'N;s/[^
]/_/g'

给我unbalanced brackets

[^[:cntrl:]]仅当没有我想要匹配的其他控制字符时才使用有效。

那么在 POSIX 中匹配除换行符之外的任何字符的正确方法是什么sed

答案1

也许我没有正确理解你的问题,但我会抓住机会回答。

如果您想匹配除换行符之外的所有内容,一个简单的正则表达式点.正是这样做的:匹配除换行符之外的任何字符。

让我们用非 gnu sed 来尝试一下:

$ cat file5
home
help
variables
compatibility

$ sed 's/./_/g' file5
____
____
_________
_____________

$ echo "one
two
three
four" |sed 's/./_/g'
___
___
_____
____

顺便说一句,你的第一个 sed 示例:

echo "one
two" | sed 'N;s/\n/_/g'

仅匹配下一个新行,而不是每个新行:

$ echo "one
> two
> three
> four" |sed 'N;s/\n/_/g'
one_two
three_four

答案2

基本正则表达式的 POSIX 规范不允许\n匹配文字换行符(我的重点在下面):

POSIX.1-2017 的 Shell 和实用程序卷在使用正则表达式的标准实用程序的单独描述中指定了它们是否允许<newline>字符匹配;<newline>如果没有另外说明,在模式或匹配文本中使用文字字符或任何等效的转义序列会产生未定义的结果

幸运的是,sed实用程序的规范包含以下文本,另有说明:

sed实用程序应支持 XBD 基本正则表达式中描述的 BRE,并添加以下内容:

[...]

  • 转义序列\n应与<newline>模式空间中的嵌入相匹配。 [...]

这允许匹配在正则表达式中使用的已嵌入模式空间(通过使用 eg )sed的文字换行符。N\n

这让我相信可以用来[^\n]匹配任何单个非换行符。这也是sedGNU 系统、OpenBSD、FreeBSD 和 Plan 9 上的实现所做的。

答案3

实际上,有一种非常巧妙的方法可以以常规方式处理这种情况sed:将换行符与一些常规字符(例如 _)交换,然后执行 [^_],然后返回。我本来想发布一个出现的问题的解决方案,但懒得发布它,但现在让我把它放在这里:

sed -e '
   /./!b

   :loop
      $q; N
   /\n$/bloop

   h

   /\ncreate table/!{
      s/\(.*\)\n.*/\1/p
      g;s/.*\(\n\)/\1/;D
   }

   g

   y/\n_/_\n/
      s/^[^_]*/test/
   y/\n_/_\n/

' input.data

上述解决方案的问题陈述。

答案4

您可以使用[[:alnum:][:punct:][:blank:]]括号表达式:

echo "one
two" | sed 'N;s/[[:alnum:][:punct:][:blank:]]/_/g'

输出:

___
___

匹配[:alnum:]所有字母数字字符,[:punct:]匹配所有标点符号并[:blank:]匹配所有水平空格。所有垂直空白都被忽略并且不匹配。

请参阅在线sed演示

相关内容