sed不给我正确的替代操作换Mac的换行 – GNU sed和BSD / OSX sed之间的差异
我使用这个引用: sed help:匹配和replace文字“\ n”(不是换行符)
我有一个文件“test1.txt”,其中包含一个stringhello \ ngoodbye
我用这个命令search并用实际换行符replace“\ n”:
sed -i '' 's/\\n/\n/g' test1.txt
但结果是: hellongbybye 。 它只是将“\ n”replace为“n”而不是实际的新行。 这和/ t的情况一样,它会留下“t”而不是标签。
''是MAC中的未定义错误: http : //mpdaugherty.wordpress.com/2010/05/27/difference-with-sed-in-place-editing-on-mac-os-x-vs-linux /
更新 :
我已经尝试了@ hek2mgl提示的两个命令:
sed -i 's/\\n/\n/g' test.txt # Or: sed -i'' 's/\\n/\n/g' test.txt
虽然他们可能与Linux操作系统,MAC操作系统,我得到了以下错误:
sed: 1: "test1.txt": undefined label 'est1.txt'
不知道为什么我不能得到这个工作。 提前致谢。
使用BSD / macOS sed
,要在s
函数调用的replacestring中使用换行符,则必须使用\
-escaped 实际换行符 – 转义序列\n
在那里不受支持(与调用的正则expression式部分不同)。
-
要么 :只需插入一个实际的换行符:
sed -i '' 's/\\n/\ /g' test1.txt
-
或者 :使用ANSI C引用的string(
$'...'
)在换行符($'\n'
;在bash
,ksh
或zsh
)中进行拼接:sed -i '' 's/\\n/\'$'\n''/g' test1.txt
相比之下, GNU sed
会识别replacestring中的\n
; 请阅读这两个实现之间差异的综合概述。
GNU sed
(Linux)与BSD / macOS sed
之间的区别
macOS使用sed
[1]的BSD版本,它在许多方面与Linux发行版附带的GNU sed
版本不同。
它们的共同点是POSIX规定的function:请参阅POSIX sed
规范。
最便携的方法是仅使用POSIXfunction ,但限制function :
- 值得注意的是,POSIX 仅支持基本的正则expression式 ,这些基本的正则expression式有很多限制(例如,根本不支持
|
(变更),不支持+
和?
)和不同的转义要求。- 警告: GNU
sed
(不带-r
),支持\|
,\+
和\?
,这不符合POSIX标准; 使用--posix
来禁用 (见下文)。
- 警告: GNU
- 仅使用POSIXfunction :
- (两个版本): 只使用
-n
和-e
选项(特别是,不要使用-E
或-r
打开对扩展正则expression式的支持) - GNU
sed
:添加选项--posix
以确保仅用于POSIXfunction(您并不严格需要此function,但如果没有它,最终可能会在不注意的情况下使用非POSIXfunction而不注意; 警告 :--posix
本身 不符合POSIX ) - 使用POSIX-onlyfunction意味着更严格的格式要求(在GNU
sed
放弃许多便利):- 控制字符序列(如
\n
和\t
通常不受支持。 - 标签和分支命令(如
b
)后面必须跟一个实际的换行符或通过一个单独的-e
选项延续。 - 详情请参阅下文。
- 控制字符序列(如
- (两个版本): 只使用
但是, 两个版本都实现了对POSIX标准的扩展 :
- 他们实现什么扩展不同 (GNU
sed
实现更多)。 - 即使是他们所实现的扩展在语法上也有 部分不同 。
如果您需要支持这两个平台(讨论差异):
- 不兼容的function:
- 不带参数的
-i
选项的使用(不带备份的就地更新)是不兼容的:- BSD
sed
:必须使用-i ''
- GNU
sed
:只能使用-i
(相当于:-i''
) – 使用-i ''
不起作用。
- BSD
-
-i
在GNUsed
和最新版本的BSDsed
(例如,在FreeBSD 10上)中明智地打开了每个input文件的行编号 ,但在10.12版本的macOS上没有 。
请注意,如果缺less-i
所有版本的数字行, 则会跨越input文件累积 。 - 如果最后一个input行没有结尾的换行符 (并被打印):
- BSD
sed
: 总是在输出上附加一个换行符 ,即使input行不结束。 - GNU
sed
: 保留尾随换行符状态 ,即仅当input行结束时才附加换行符。
- BSD
- 不带参数的
- 共同特点:
- 如果你将你的
sed
脚本限制在BSDsed
支持的地方,他们通常也可以在GNUsed
工作 – 除了在-E
上使用平台特定的扩展正则expression式特性之外。 显然,你也会放弃特定于GNU版本的扩展。 请参阅下一节。
- 如果你将你的
由BSD版本的更严格的要求驱动的跨平台支持指南(OS X / BSD,Linux) :
请注意,我正在分别使用BSD和GNU版本的sed
使用shorthands macOS和Linux ,因为它们是每个平台上的股票版本。 但是,可以在macOS上安装GNU sed
,例如,用brew install gnu-sed
使用Homebrew 。
注意 : 除了使用-r
和-E
标志 ( 扩展正则expression式)之外,以下指令等同于编写符合POSIX标准的 sed
脚本。
- 对于POSIX合规性,你必须限制自己的POSIX BREs( 基本正则expression式) ,不幸的是,顾名思义,这是非常基本的。
警告 :不要假设\|
,\+
和\?
支持:虽然GNUsed
支持它们(除非使用--posix
),BSDsed
不支持 – 这些function不符合POSIX标准。
而\+
和\?
可以用 POSIX兼容的方式进行仿真 :
\{1,\}
为\+
,
\{0,1\}
为\?
,
\|
(替代) 不能 ,不幸的是。 -
对于更强大的正则expression式, 使用
-E
(而不是-r
)来支持ERE( 扩展的正则expression式) (GNUsed
不是文件-E
,但是它在那里工作,作为-r
的别名; 更新版本的BSDsed
,比如在FreeBSD 10上,现在也支持-r
,但是10.10版本的macOS版本不支持 )。
警告 :即使使用-r
/-E
表示您的命令在定义上不符合POSIX,您仍然必须将自己限制为POSIX ERE(扩展正则expression式) 。 可悲的是,这意味着你将无法使用一些有用的结构,特别是:- 字边界断言,因为它们是特定于平台的 (例如,在Linux上是
\<
,在OS X上是[[:<]]
。 - 在正则expression式中反向引用(而不是在函数调用的replacestring中对“捕获组匹配”的“后向引用”),因为BSD
sed
在扩展正则expression式中不支持它们(但是,奇怪的是, 基本的,POSIX授权的)。
- 字边界断言,因为它们是特定于平台的 (例如,在Linux上是
-
控制字符转义序列,如
\n
和\t
:- 在正则expression式中 (既包括select模式,也包括
s
函数的第一个参数),假定只有\n
被识别为一个转义序列(很less使用,因为模式空间通常是一行(不终止\n
)而不是在字符类中 ,因此,例如[^\n]
不起作用;(如果您的input不包含\t
以外的控制字符,可以用[[:print:][:blank:]]
来模拟[^\n]
[[:print:][:blank:]]
;否则,拼接控制字符为文字 [2] ) – 通常,包含控制字符作为文字 ,要么通过拼接在ANSI C引号string (例如,$'\t'
)支持它(bash,
ksh,zsh
),或者通过使用printf
命令replace (例如"$(printf '\t')"
) 。- 仅限Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
- macOS 和 Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- 仅限Linux:
-
在与
s
命令一起使用的replacestring中 , 假设不支持控制字符转义序列 ,所以再次包含控制字符。 作为文字 ,如上所述。- 仅限Linux:
sed 's/-/\t/' <<<$'ab' # -> 'a<tab>b'
- macOS 和 Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- 仅限Linux:
-
对于
i
和a
函数的文本参数同上: 不要使用控制字符序列 – 见下文。
- 在正则expression式中 (既包括select模式,也包括
- 标签和分支 :标签以及
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'
- OR(多个
-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'
- macOS 和 Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
- 注意:
- 没有
-e
,text参数在macOS(bug?)输出中莫名其妙地不是换行符。 - 不要在文本参数中使用控制字符转义,例如
\n
和\t
,因为它们只在Linux上受支持。 - 如果文本参数因此具有实际的内部换行符,则将其隐藏起来。
- 如果你想在文本参数之后放置额外的命令,你必须用一个(非转义的)换行符(不pipe是字面还是拼接)来终止它,或者继续单独的
-e
选项(这是一个适用于所有版本的一般要求) 。
- 没有
- 仅限Linux:
- 在函数列表 (
{...}
包含的多个函数调用)中, 一定要在closures}
之前终止最后一个函数, 。- 仅限Linux:
-
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
- macOS 和 Linux:
-
sed -n '1 {p;q;}' <<<$'a\nb'
从BSD sed
完全没有GNU sed
特有的function :
如果你需要支持这两个平台,你将错过GNUfunction:
-
各种正则expression式匹配和replace选项 (包括线select模式和
s
函数的第一个参数):- 对于不区分大小写的正则expression式匹配的
I
选项(令人难以置信的是,BSDsed
根本不支持这一点)。 - 多行匹配的
M
选项(其中^
/$
匹配每行的开始/结尾) - 有关特定于
s
function的其他选项,请参阅https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- 对于不区分大小写的正则expression式匹配的
-
逃逸序列
-
在
s///
函数的replace参数中,replace相关的转义序列(如\u
允许在限制范围内进行子string操作 ; 例如sed 's/^./\u&/' <<<'dog' # -> 'Dog'
– 请参阅http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022 -命令 -
控制字符转义序列:除了
\n
,\t
,…,基于码点的转义; 例如,所有以下转义(hex,八进制,十进制)表示单引号('
):\x27
,\o047
,\d039
– 请参阅https://www.gnu.org/software/sed/manual/ sed.html#逃逸
-
-
地址扩展 ,比如
first~step
匹配每first~step
行,addr, +N
匹配addr
N行, – 见http://www.gnu.org/software/sed/manual/sed。; HTML#地址
[1] macOS sed
版本比其他类似BSD的系统(如FreeBSD和PC-BSD)上的版本要旧 。 不幸的是,这意味着你不能认为在FreeBSD上工作的特性,例如,在macOS上也是一样的。
[2] ANSI C引用的string$'\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'']
这似乎有点奇怪,但请尝试:
sed -i '' 's/\\n/\ /g' test1.txt
即,使用一个实际的换行符而不是\n
。
解释是你有一个奇怪的sed
! 有关详细信息,请参阅mac sed手册: https : //developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/sed.1.html
在那里的s
命令的描述中,它说:
A line can be split by substituting a newline character into it. To specify a newline character in the replacement string, precede it with a backslash.
另外,在-i
选项的描述中,它表示扩展名不是可选的,如果你不需要,你必须指定一个空的参数。 所以一切都最终有意义!