问题描述
|
如何在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”)也类似地工作。
背景信息:
POSIXsed
规范:http://man.cx/sed
BSDsed
(也在macOS上使用)保持接近此规格,而GNUsed
提供了许多扩展。
可以在https://stackoverflow.com/a/24276470/45375上找到GNUsed
和BSDsed
之间的区别的摘要。
,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