在sed中插入换行Mac OS X

问题描述

| 如何在sed的替换部分插入换行符? 此代码不起作用:
sed \"s/\\(1234\\)/\\n\\1/g\" input.txt > output.txt
其中input.txt是:
test1234foo123bar1234
和output.txt应该是:
test
1234foo123bar
1234
但是我明白了
testn1234foo123barn1234
注意: 这个问题专门针对Mac OS X版本的“ sed”,并且社区已经注意到它的行为与Linux版本不同。     

解决方法

您的sed版本显然不支持RHS(替代的右侧)中的right4 side。您应该阅读Eric Pement维护的SED FAQ,以选择一种可能的解决方案。我建议尝试先插入文字换行符。 下面是它的报价。 4.1。如何在替换的RHS中插入换行符? sed的几种版本允许将“ 4”直接键入RHS,然后将其转换为输出的换行符:ssed,gsed302a +,gsed103(带有“ 6”开关),sed15 +,sedmod和UnixDOS sed。最简单的解决方案是使用这些版本之一。 对于其他版本的sed,请尝试以下操作之一: (a)如果从Bourne外壳键入sed脚本,如果脚本使用“单引号”,则使用一个反斜杠
\\
,如果脚本需要使用“双引号”,则使用两个反斜杠
\\\\
。在下面的示例中,请注意第二行的前导ѭ9是由shell生成的,以提示用户进行更多输入。用户键入斜杠,单引号,然后按Enter以终止命令:
 [sh-prompt]$ echo twolines | sed \'s/two/& new\\
 >/\'
 two new
 lines
 [bash-prompt]$
(b)使用脚本文件中带有一个反斜杠“ 7”的脚本文件,然后紧跟一个换行符。这会将换行符嵌入到“替换”部分。例:
 sed -f newline.sed files

 # newline.sed
 s/twolines/two new\\
 lines/g
某些版本的sed可能不需要结尾的反斜杠。如果是这样,请将其删除。 (c)插入一个未使用的字符,并将输出通过tr传递给管道:
 echo twolines | sed \'s/two/& new=/\' | tr \"=\" \"\\n\"   # produces
 two new
 lines
(d)使用
G
命令: G附加一个换行符,再加上保留空间的内容到模式空间的末尾。如果保留空间为空,则无论如何都会添加换行符。换行符以the4ѭ的形式存储在模式空间中,可以通过分组
\\(...\\)
进行寻址并在RHS中移动。因此,要更改先前使用的“ twolines”示例,以下脚本将起作用:
 sed \'/twolines/{G;s/\\(two\\)\\(lines\\)\\(\\n\\)/\\1\\3\\2/;}\'
(e)插入整行,而不是分行: 如果不更改行而仅在模式之前或之后插入完整的行,则此过程会容易得多。使用
i
(插入)或
a
(追加)命令,通过外部脚本进行更改。要在与正则表达式匹配的每一行之前插入“ 20”:
 /RE/i This line is new               # HHsed,sedmod,gsed 3.02a
 /RE/{x;s/$/This line is new/;G;}     # other seds
上面的两个示例旨在作为从控制台输入的“单行”命令。如果使用sed脚本,则在所有版本的sed上都应立即使用ѭ22by后跟文字换行符。此外,命令“ 23”仅在保留空间已经为空(默认情况下)时才有效。 要在每条与正则表达式匹配的行之后追加
This line is new
 /RE/a This line is new               # HHsed,gsed 3.02a
 /RE/{G;s/$/This line is new/;}       # other seds
要在每条与正则表达式匹配的行之后追加2个空行:
 /RE/{G;G;}                    # assumes the hold space is empty
要将与正则表达式匹配的每一行替换为5个空行:
 /RE/{s/.*//;G;G;G;G;}         # assumes the hold space is empty
(f)尽可能使用Use28 the命令: 在sed的某些Unix版本上(不是GNU sed!),尽管
s///
命令在RHS中不接受
\\n
,但
y///
命令却可以。如果您的Unix sed支持,则可以在inserted32ѭ之后插入一个换行符(不能移植到GNU sed或其他sed中):
 s/aaa/&~/; y/~/\\n/;    # assuming no other \'~\' is on the line!
    ,这是一个单行解决方案,可与任何POSIX兼容的
sed
(包括macOS上的FreeBSD版本)一起使用,假设您的外壳是
bash
ksh
zsh
sed \'s/\\(1234\\)/\\\'$\'\\n\'\'\\1/g\' <<<\'test1234foo123bar1234\'
请注意,您可以使用一个用ANSI C引号的字符串作为整个
sed
脚本,即
sed $\'...\' <<<
,但这需要对所有
\\
实例进行
\\
转义(将它们加倍),这非常麻烦并且妨碍了可读性,如@tovk \'所示。的答案)。
$\'\\n\'
代表换行符,并且是ANSI C引号的一个实例,它使您可以创建带有控制字符转义序列的字符串。 上面的代码将ANSI C引用的字符串拼接到
sed
脚本中,如下所示: 脚本被简单地分成2个单引号字符串,而ANSI C引号字符串则被卡在两个半部分之间:
\'s/\\(1234\\)/\\\'
是第一半-注意,它以
\\
结尾,以便转义将作为下一个字符插入的换行符。 (此转义对于将换行符标记为替换字符串的一部分,而不是被解释为命令的结尾是必需的)。
$\'\\n\'
是用ANSI C引号表示的换行符,在将脚本传递给
sed
之前,shell会扩展为实际的换行符。
\'\\1/g\'
是第二半。 请注意,此解决方案对于其他控制字符(例如,表示菜单字符的“ 50”)也类似地工作。 背景信息: POSIX
sed
规范:http://man.cx/sed BSD
sed
(也在macOS上使用)保持接近此规格,而GNU
sed
提供了许多扩展。 可以在https://stackoverflow.com/a/24276470/45375上找到GNU
sed
和BSD
sed
之间的区别的摘要。     ,Perl提供了更丰富的“扩展”正则表达式语法,在此很有用:
perl -p -e \'s/(?=1234)/\\n/g\'
表示\“用模式1234 \替换换行符以零宽度匹配”。这避免了必须捕获并重复使用带有反向引用的表达式。     ,我可以说服
sed
的solaris版本(in35ѭ):
echo test1234foo123bar1234 | sed \'s/\\(1234\\)/\\
\\1/g\'
(您必须在反斜杠后直接放置换行符)。 在ѭ60中,我不得不再加一个反斜杠:
echo test1234foo123bar1234 | sed \'s/\\(1234\\)/\\\\
\\1/g\'
nu34ѭ的Gnu版本仅使用
\\n
即可工作:
echo test1234foo123bar1234 | sed \'s/\\(1234\\)/\\n\\1/g\'
    ,获取GNU sed。
$ brew install gnu-sed
然后您的命令将按预期工作:
$ gsed \"s/\\(1234\\)/\\n\\1/g\" input.txt
test
1234foo123bar
1234
nb:您也可能由于mac端口而获得了GNU sed。     ,不幸的是,对我来说,
sed
似乎忽略了替换字符串中的
\\n
$ echo test1234foo123bar1234 | sed \"s/\\(1234\\)/\\n\\1/g\"
testn1234foo123barn1234
如果您也遇到这种情况,可以使用以下替代方法:
$ echo test1234foo123bar1234 | sed \"s/\\(1234\\)/\\\\`echo -e \'\\n\\r\'`\\1/g\"
这应该可以在任何地方工作,并且会产生:
test
1234foo123bar
1234
对于以
input.txt
文件作为输入和
output.txt
作为输出的示例,请使用:
$ sed \"s/\\(1234\\)/\\\\`echo -e \'\\n\\r\'`\\1/g\" input.txt > output.txt
    ,尝试这个:
$ echo test1234foo123bar1234 | sed \"s/\\(1234\\)/\\n\\1/g\"
test
1234foo123bar
1234
来自Sed Gnu doc
g
    Apply the replacement to all matches to the regexp,not just the first. 
    ,您还可以使用Bash的“ 77”功能:
man bash | less -p \"\\\\$\'\"

printf  \'%s\' \'test1234foo123bar1234\'  | sed $\'s/\\\\(1234\\\\)/\\\\\\n\\\\1/g\'
    ,命令中间的换行符可能有点笨拙:
$ echo abc | sed \'s/b/\\
/\'
a
c
这里有两个解决方案,我认为应该很方便 (应适用于任何POSIX兼容的
sh
printf
sed
): 解决方案1: 请记住在此处将
printf
的所有
\\
%
字符转义:
$ echo abc | sed \"$(printf \'s/b/\\\\\\n/\')\"
a
c
为避免将
printf
转义为
\\
%
字符:
$ echo abc | sed \"$(printf \'%s\\n%s\' \'s/b/\\\' \'/\')\"
a
c
解决方案2: 创建一个包含换行符的变量,如下所示:
newline=\"$(printf \'\\nx\')\"; newline=\"${newline%x}\"
或像这样:
newline=\'
\'
然后像这样使用它:
$ echo abc | sed \"s/b/\\\\${newline}/\"
a
c