对于 BSD/macOS sed,要在s 函数调用的替换字符串 中使用换行符,您必须使用\-转义实际 换行 - 转义序列\n 不支持(与调用的regex 部分不同)。
-
任一:只需插入一个实际换行符:
sed -i '' 's/\\n/\
/g' test1.txt
-
或:使用ANSI C-quoted string ($'...') 拼接换行符($'\n';适用于bash、ksh 或zsh):
sed -i '' 's/\\n/\'$'\n''/g' test1.txt
GNU sed,相比之下,确实在替换字符串中识别 \n;请继续阅读以全面了解这两种实现之间的差异。
GNU sed (Linux) 和 BSD/macOS sed 之间的区别
macOS 使用 sed[1] 的 BSD 版本,这在许多方面与 GNU em> Linux 发行版附带的sed 版本。
它们的共同点是POSIX规定的功能:参见the POSIX sed spec.
最便携的方法是仅使用 POSIX 功能,但是,限制了功能:
- 值得注意的是,POSIX 指定仅支持 basic 正则表达式,它有许多限制(例如,根本不支持
|(替代),不直接支持对于+ 和?) 以及不同的转义要求。
- 警告:GNU
sed(没有-r),确实支持\|、\+和\?,这不符合POSIX;使用--posix 禁用(见下文)。
-
仅使用 POSIX 功能:
- (两个版本):仅使用
-n 和-e 选项(值得注意的是,不要使用-E 或-r 来开启支持对于扩展正则表达式)
- GNU
sed:添加选项 --posix 以确保仅 POSIX 功能(您并不严格需要它,但如果没有它,您最终可能会在不注意的情况下无意中使用非 POSIX 功能;警告 em>:--posix 本身 不 POSIX兼容)
- 使用仅 POSIX 功能意味着更严格的格式要求(放弃 GNU
sed 中提供的许多便利):
- 一般不支持控制字符序列,例如
\n 和 \t。
- 标签和分支命令(例如,
b)必须后跟 实际换行符或通过单独的 -e 选项继续。
- 详见下文。
但是,两个版本都实现了对 POSIX 标准的扩展:
-
他们实现的扩展是什么不同(GNU
sed 实现的更多)。
- 即使是它们都实现的那些扩展在语法上也有部分不同。
如果您需要同时支持 BOTH 平台(讨论差异):
-
不兼容功能:
- 使用
-i 选项没有一个参数(就地更新没有备份)是不兼容的:
- BSD
sed: 必须使用 -i ''
- GNU
sed:必须只使用 -i(等效:-i'') - 使用 -i '' 不起作用。
-
-i 明智地在 GNU sed 和 最近 版本中打开 per-input-file 行编号 BSD sed(例如,在 FreeBSD 10 上),但 在 10.15 之后的 macOS 上没有。
请注意,在没有-i 所有 版本的情况下,输入文件中累积的行数。
- 如果 last 输入行 not 有尾随换行符(并且被打印):
- BSD
sed:总是在输出中附加一个换行符,即使输入行不是以一个结尾。
- GNU
sed:保留尾随换行状态,即,仅当输入行以 1 结尾时,它才会附加换行符。
-
常见特点:
- 如果您将
sed 脚本限制在 BSD sed 支持的范围内,它们通常也可以在 GNU sed 中工作 - 除了使用特定于平台的 扩展 正则表达式功能与-E。显然,您还将放弃特定于 GNU 版本的扩展。请参阅下一节。
跨平台支持指南(macOS/BSD、Linux),由更严格的 BSD 版本要求驱动:
请注意,我分别使用简写 macOS 和 Linux 表示 sed 的 BSD 和 GNU 版本,因为它们是每个版本的库存版本平台。但是,可以在 macOS 上安装 GNU sed,例如,使用 Homebrew 和 brew install gnu-sed。
注意:使用-r 和-E 标志时除外(扩展正则表达式),下面的说明相当于编写 POSIX 兼容 sed 脚本。
- 为了符合 POSIX,您必须将自己限制在 POSIX BREs (basic regular expressions),不幸的是,顾名思义,这是非常基本的。
警告:不要假设 \|、@支持 987654394@ 和 \?:虽然 GNU sed 支持它们(除非使用 --posix),但 BSD sed 不支持 - 这些功能不 POSIX 兼容。
虽然 \+ 和 \? 可以以符合 POSIX 的方式模拟:
\{1,\} for \+,
\{0,1\} for @ 987654404@,
\|(替代)不能,很遗憾。
-
对于更强大的正则表达式,使用-E(而不是-r)来支持ERE(扩展正则表达式)( GNU sed 没有记录 -E,但它作为 -r 的别名在那里工作;更新 版本的 BSD sed,例如在 FreeBSD 10 上,现在也支持 @ 987654412@,但 10.10 的 macOS 版本不是)。
警告:即使使用 -r / -E 意味着您的命令是由定义不 POSIX 兼容,您仍必须限制自己使用POSIX EREs (extended regular expressions)。遗憾的是,这意味着您将无法使用几个有用的构造,特别是:
- 字边界断言,因为它们是特定于平台的(例如,Linux 上的
\<,OS X 上的[[:<]])。
- 反向引用在正则表达式中(与
s 函数调用的替换字符串中捕获组匹配的“反向引用”相反),因为 BSD sed 没有t 在 extended 正则表达式中支持它们(但奇怪的是,在 basic 正则表达式中支持它们,它们是 POSIX 强制的)。
-
控制字符转义序列,例如\n 和\t:
-
标签和分支:
b 和 t 函数的标签以及标签名称参数必须后跟 文字换行符或拼接的$'\n'。或者,使用多个 -e 选项并在标签名称之后终止每个选项。
- 仅限 Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
- macOS 和 Linux:
- 任一(实际换行符):
sed -n '/a/ bLBL
d; :LBL
p' <<<$'a\nb'
- OR(拼接
$\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'
- 函数
i 和 a 用于插入/附加文本:函数名称后跟 \,后跟 文字 换行符或在指定文本参数之前拼接$'\n'。
- 仅限 Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
- macOS 和 Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
- 注意:
- 没有
-e,文本参数在 macOS 的输出中莫名其妙地没有换行符终止(错误?)。
-
请勿在文本参数中使用控制字符转义,例如
\n 和 \t,因为它们仅在 Linux 上受支持。
- 如果文本参数因此具有实际的内部换行符,
\-转义它们。
- 如果您想在文本参数之后放置其他命令,您必须使用(未转义的)换行符(无论是文字还是拼接)终止它,或者继续使用单独的
-e 选项(这是适用的一般要求所有版本)。
-
函数lists内部({...}中包含的多个函数调用),一定要同时终止last函数,收盘前},与;。
- 仅限 Linux:
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
- macOS 和 Linux:
sed -n '1 {p;q;}' <<<$'a\nb'
使用 -f 选项(从文件中读取命令),只有 GNU sed 支持 - 作为标准输入的占位符; 使用-f /dev/stdin 可移植地从标准输入读取命令,包括从here-documents 读取命令(假设您的平台支持/dev/stdin,现在通常是这种情况)。
GNU sed 特有的功能完全从 BSD sed 中消失:
如果您需要同时支持这两个平台,您会错过的 GNU 功能:
[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:] 结合使用,以获得非常强大的 @ 模拟987654502@:
'[[: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'']