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'']