不同操作系统中简单sed全局替换的奇怪行为



我偶然发现了以下sed命令的一个小问题,该命令去掉了一行的前导和尾部空格字符,并将其封装在双引号中:

printf '%sn' ' hello ' ' hello' 'hello ' hello  | sed -E 's/^ *| *$/"/g'

结果是:

  • 在Linux上:
"hello"
"hello"
"hello"
"hello"
  • 在macOS上:
"hello"
"hello
"hello"
"hello
FreeBSD上的
"hello"
"
"hello"
"hello"

我并不是真的在寻找解决方案,因为我有适用于所有平台的解决方案(不过我对其他方案持开放态度(:

sed 's/^ */"/;s/ *$/"/'
awk '{gsub(/^ *| *$/,""")}1' # the culprit works fine with awk

我的问题是:我对sed命令的理解是错误的吗?或者这可以被认为是macOS和FreeBSDsed实现中的一个错误吗?

Sed实现差异很大

sed有很多不同的版本,通常情况下,你不应该期望不同版本之间有标准化的行为,尤其是在不同的操作系统上。sed常见问题解答可以说已经过时了,但它很好地说明了有多少不同的实现。Open Group还提供了sed的基本规范,这主要是因为不包括-E标志和对扩展正则表达式(ERE(的支持。

GNU sed可以说更具可移植性,因为它可以被编译,并且在任何可以使用GCC构建的系统上都会有类似的行为,任何所需的扩展,如ERE和PCRE支持,并且行结尾相同或在表达式中正确解释。由于大多数sed实现实际上都是POSIX规范的超集,因此在其他方面,您将受制于您所使用的特定sed:

  1. 实际的POSIX合规性,因为并非所有的shell、OS或userland工具都旨在完全符合POSIX,无论是否带有标志;以及
  2. 您的特定用例或表达式可能需要的任何附加功能

如果可移植性是一个主要问题,并且你不能依赖GNU sed,那么你可能想看看你是否可以依赖其他类似sed的超集,比如Perl或Ruby模式,它们使用命令行标志来进行面向行的处理,但背后有更强大的正则表达式引擎。

最新更新