BSD macOS Sed 与 GNU Sed 对比 POSIX Sed 规范
**macOS 使用了 sed [1] 的 BSD 版本,它在很多方面都与 Linux 发行版附带的 GNU sed 版本不同。 ** ****
它们的共同点是 POSIX 规定的功能 :参见 POSIX sed 规范。
在最轻便的方法是使用 POSIX 才有的功能,然而,限制功能 :
-
值得注意的是,POSIX 仅 指定对基本正则表达式的****支持**,这些正则表达式具有许多限制(例如,根本不支持
|(交替),不直接支持+和?)以及不同的转义要求。- 警告: GNU
sed(无-r),确实支持\|,\+和\?,这是不符合 POSIX 标准; 使用--posix禁用 (见下文)。
- 警告: GNU
-
仅使用 POSIX 功能 :
-
(两个版本): 仅使用
-n和-e选项(特别是,不要使用-E或-r来启用对扩展正则表达式的支持 ) -
GNU
sed:添加选项--posix,以确保只有 POSIX 的功能(你不严格需要这一点,但没有它,你最终可能会不小心使用了非 POSIX 功能没有注意到; 警告 :--posix本身是不符合 POSIX 标准) -
仅使用 POSIX 功能意味着更严格的格式化要求(放弃 GNU
sed中提供的许多便利):- 通常不支持控制字符序列,例如
\n和\t。 - 标签和分支命令(例如,
b) 必须后跟实际的换行符或通过单独的-e选项继续。 - 请参阅下文了解详情。
- 通常不支持控制字符序列,例如
-
但是,这两个版本都实现了 POSIX 标准的扩展** :**
- 什么他们执行的扩展名不同 (GNU
sed实现更多)。 - 甚至那些他们都实现的扩展部分语法也不同。
如果你需要支持 BOTH 平台(讨论差异):
-
不兼容的功能:
-
使用不带参数的 **
-i选项 **** (没有备份的就地更新)是不兼容的:- BSD
sed:必须使用-i '' - GNU
sed:必须使用-i(相当于:-i'') - 使用-i ''不起作用。
- BSD
-
-i**** 在 GNUsed和 BSDsed的最新版本 (例如,在 FreeBSD 10 上) 明智地打开每个输入文件行编号,但是从 10.12 开始不在 macOS 上。请注意,在没有-i的情况下,所有版本的数字都会在输入文件中累积**。 ** ****
-
如果最后输入线并不*具有尾随换行符* (和打印):
- BSD
sed: 总是在输出上附加换行符,即使输入行没有以一个结尾。 - GNU
sed: 保留尾部换行符状态,即仅当输入行以一个结尾时才附加换行符。
- BSD
-
-
共同特征:
- 如果你将
sed脚本限制为 BSDsed所支持的脚本,它们通常也可以在 GNUsed中工作 - 除了使用-E使用特定于平台的扩展正则表达式功能之外。显然,你还将放弃特定于 GNU 版本的扩展。见下一节。
- 如果你将
跨平台支持指南 (OS X / BSD,Linux),受 BSD 版本更严格的要求驱动 :
请注意,下面偶尔使用短序 macOS 和 Linux 来分别引用 sed 的 BSD 和 GNU 版本,因为它们是每个平台上的库存版本。但是,可以在 macOS 上安装 GNU sed,例如,使用带有 brew install gnu-sed 的 Homebrew 。
注意 : 除非使用 -r 和 -E 标志 ( 扩展正则表达式),否则下面的说明相当于编写符合 POSIX 标准的 sed 脚本。
-
对于 POSIX 兼容性,你必须限制自己使用 POSIX BRE( 基本正则表达式) ,不幸的是,顾名思义,它非常基本。
警告 :不要认为支持\|,\+和\?:虽然 GNUsed支持它们(除非使用--posix),BSDsed不支持 - 这些功能不符合 POSIX 标准。
虽然\+和\?可以以符合 POSIX 标准的方式进行模拟 :
\{1,\}为\+,
\{0,1\}为\?,但
\|(交替) 不能。 -
对于更强大的正则表达式,使用
-E(而不是-r)来支持 ERE( 扩展正则表达式) (GNUsed不记录-E,但它确实作为-r的别名工作; 更新版本的 BSDsed,例如 on FreeBSD 10,现在也支持-r,但是 10.12 的 macOS 版本没有 )。
警告 :尽管使用-r/-E意味着你的命令根据定义并不符合 POSIX,但你仍必须限制自己使用 POSIX ERE(扩展正则表达式) 。遗憾的是,这意味着你将无法使用几种有用的结构,特别是:- 字边界断言,因为它们是特定于平台的 (例如,Linux 上的
\<,OS X 上的[[:<]])。 - 正则表达式中的反向引用 (而不是在
s函数调用的替换字符串中对捕获组匹配的反向引用),因为 BSDsed在扩展正则表达式中不支持它们 (但是,奇怪的是,在基本的,它们是 POSIX 规定的)。
- 字边界断言,因为它们是特定于平台的 (例如,Linux 上的
-
控制字符转义序列,如
\n和\t:-
在正则表达式 (用于行选择的模式和
s函数的第一个参数)中,假设只有\n被识别为转义序列(很少使用,因为模式空间通常是单行 (不终止\n),但不是在一个字符类中,所以,例如,[^\n]不起作用;(如果你的输入不包含控制字符。除了\t,你可以用[[:print:][:blank:]]模拟[^\n];否则,拼接控制字符作为文字 [2] ) - 通常,将控制字符包含为文字**,或者通过拼接的 ANSI C 引用的字符串 (例如,$'\t')支持它(bash,ksh,zsh),或者通过使用printf命令替换 (例如,$(printf '\t'))** 。- 仅限 Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b' - OSX 和 Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- 仅限 Linux:
-
在与
s命令一起使用的替换字符串中,假设支持 NO 控制字符转义序列,因此,再次包括控制字符。作为文字,如上所述。- 仅限 Linux:
sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b' - macOS 和 Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- 仅限 Linux:
-
同上的文本参数到
i和a功能 : 不使用控制字符序列 -见下文。
-
-
标签和分支 :标签以及
b和t函数的 label-name 参数必须后跟文字换行符或拼接的$'\n'。或者,使用多个-e选项并在标签名称后面的每个选项终止。- 仅限 Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a' - macOS 和 Linux:
- (实际换行):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb' - 或(拼接
$\n实例):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb' - 或(多个
-e选项):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- (实际换行):
- 仅限 Linux:
-
函数
i和a用于插入/附加文本 : 按照\的函数名称, 在指定文本参数之前跟随文字换行符或拼接的$'\n'** 。- 仅限 Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b' - OSX 和 Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb' - 注意:
- 如果没有
-e,text 参数莫名其妙地不会在 macOS 输出上换行(bug?)。 - 不要在 text 参数中**使用控制字符转义符,**例如
\n和\t,因为它们仅在 Linux 上受支持。 - 因此,如果 text 参数具有实际的内部换行符,则
\- 转义它们。 - 如果要在 text 参数后面添加其他命令,则必须使用(非转义)换行符(无论是文字还是拼接)终止它,或者继续使用单独的
-e选项(这是适用于所有版本的一般要求)。
- 如果没有
- 仅限 Linux:
-
在函数列表内 (
{...}中包含多个函数调用),请务必在关闭}之前使用;终止最后一个函数。- 仅限 Linux:
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
- macOS 和 Linux:
sed -n '1 {p;q;}' <<<$'a\nb'
- 仅限 Linux:
**** BSD sed 完全缺少 GNU sed 特有的功能 :
如果你需要支持这两个平台,你将错过的 GNU 功能:
-
各种正则表达式匹配和替换选项 (用于行选择的模式和
s函数的第一个参数):- 用于 case-INsensitive 正则表达式匹配的
I选项(令人难以置信的是,BSDsed根本不支持这一点)。 - 用于多行匹配的
M选项(其中^/$匹配每行的开头/结尾 ) - 有关
s功能特有的其他选项,请参阅 https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- 用于 case-INsensitive 正则表达式匹配的
-
转义序列
-
替换相关的转义序列,如
\u,在s///函数的替换参数中允许子字符串操作,在限制范围内; 例如,sed 's/^./\u&/' <<<'dog' # -> 'Dog'- 请参阅 http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command -
控制字符转义序列:除了
\n,\t,…,基于代码点的转义; 例如,所有以下转义(十六进制,八进制,十进制)代表单引号('):\x27,\o047,\d039- 请参阅 https://www.gnu.org/software/sed/manual/sed.html #Escapes
-
-
地址扩展,例如
first~step匹配每一步,addr, +N匹配addr之后的 N 行,… - 请参阅 http://www.gnu.org/software/sed/manual/sed.html#Addresses
[1] macOS sed 版本比其他类似 BSD 的系统(如 FreeBSD 和 PC-BSD)的版本旧。不幸的是,这意味着你不能假设在 FreeBSD 中工作的功能在 macOS 上可以[相同]工作。
[2] ANSI C 引用的字符串 $'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177'包含除\n(和 NUL)之外的所有 ASCII 控制字符,因此你可以将它与 [:print:] 结合使用,以实现非常强大的 [^\n] 仿真:
'[[:print:]'$'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177'']