我注意到,如果将\n添加到使用sed替代的模式中,则该模式不匹配。示例:

$ cat > alpha.txt
This is
a test
Please do not
be alarmed

$ sed -i'.original' 's/a test\nPlease do not/not a test\nBe/' alpha.txt

$ diff alpha.txt{,.original}

$ # No differences printed out


我该如何使用它?

评论

此处的智能解决方法:unix.stackexchange.com/a/445666/61742。当然不是表演!根据您的需要执行替换的其他不错的选择可能是awk,perl和python。还有许多其他功能,但是我相信awk在各种Linux发行版中是最通用的(例如)。谢谢!

#1 楼

在最简单的sed调用中,它在模式空间中即一行文本。 1行\n从输入中分隔文本。模式空间中的单行没有\n ...这就是为什么您的正则表达式找不到任何内容的原因。

您可以将多个行读入模式空间并以惊人的方式很好地进行操作,但是要比平常付出更多的努力。 sed命令摘要的链接。这是我找到的最好的,并且让我滚动。

但是,一旦开始使用sed的微命令,就忘记了“单线”的想法。像结构化程序一样对它进行布局很有用,直到您得到它的感觉为止。它非常简单,而且同样不寻常。您可以将其视为文本编辑的“汇编语言”。

摘要:将sed用于简单的事情,也许更多,但是总的来说,当它超出了单行的工作范围时,大多数人会喜欢其他东西...
ll让别人提出其他建议。.我真的不确定最好的选择是什么(我会使用sed,但这是因为我不太了解perl。)


sed '/^a test$/{
       $!{ N        # append the next line when not on the last line
         s/^a test\nPlease do not$/not a test\nBe/
                    # now test for a successful substitution, otherwise
                    #+  unpaired "a test" lines would be mis-handled
         t sub-yes  # branch_on_substitute (goto label :sub-yes)
         :sub-not   # a label (not essential; here to self document)
                    # if no substituion, print only the first line
         P          # pattern_first_line_print
         D          # pattern_ltrunc(line+nl)_top/cycle
         :sub-yes   # a label (the goto target of the 't' branch)
                    # fall through to final auto-pattern_print (2 lines)
       }    
     }' alpha.txt  



这里是相同的脚本,浓缩为明显更难阅读和使用的脚本,但是有些人怀疑将其称为单行

sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt



这是我的命令“备忘单”

:  # label
=  # line_number
a  # append_text_to_stdout_after_flush
b  # branch_unconditional             
c  # range_change                     
d  # pattern_delete_top/cycle          
D  # pattern_ltrunc(line+nl)_top/cycle 
g  # pattern=hold                      
G  # pattern+=nl+hold                  
h  # hold=pattern                      
H  # hold+=nl+pattern                  
i  # insert_text_to_stdout_now         
l  # pattern_list                       
n  # pattern_flush=nextline_continue   
N  # pattern+=nl+nextline              
p  # pattern_print                     
P  # pattern_first_line_print          
q  # flush_quit                        
r  # append_file_to_stdout_after_flush 
s  # substitute                                          
t  # branch_on_substitute              
w  # append_pattern_to_file_now         
x  # swap_pattern_and_hold             
y  # transform_chars                   


评论


现在射杀我。有史以来最糟糕的语法!

–吉利
2014年5月20日19:51

这是一个很棒的解释,但我倾向于同意@Gili。

– gatoatigrado
2014年5月21日在20:44

您的备忘单已包含所有内容。

– Konsolebox
2014年7月19日在12:30

您无需在此处使用标签来使用t命令-当未提供标签时,默认情况下将分支到脚本的末尾。所以sed'/ ^ a test $ / {$!{N; s / ^ a test \ n请不要$ / not test \ nBe /; t; P; D}}'alpha.txt与您的完全相同在所有情况下都指挥。当然,对于这个特定文件,sed'/test/{N;s/.*/not a test \ nBe /}'alpha.txt也可以做同样的事情,但是我的第一个示例在逻辑上等效于所有可能的文件。另请注意,替换字符串中的\ n不会产生换行符;您需要反斜杠`\`,然后再加上实际的换行符来执行此操作。

–通配符
15-10-24在13:13



请注意,语法是GNU特有的(#命令与上一个命令不分隔,在s的RHS中为\ n)。使用GNU sed,您还可以使用-z来使用NUL分隔记录(然后在整个输入中输入文本(如果定义为不包含NUL))。

–StéphaneChazelas
16年8月11日在6:33

#2 楼

使用perl而不是sed-pi -e是标准的“就地替换”命令行序列,并且-0777导致perl吞噬整个文件。有关更多信息,请参见perldoc perlrun。

评论


谢谢!对于多行工作,perl胜出了!我最终使用`$ perl -pi -e's / bar / baz /'fileA`来就地更改文件。

–尼古拉斯·托利·科特雷尔
2013年2月4日14:36

原始海报要求sed并使用awk或perl进行回复是很常见的。我认为这不是主题,因此,抱歉,我开了一分。

– Rho Phi
15年8月26日在22:07

+1并不同意罗伯托。通常,问题的措辞是专门针对更好的方法的无知。当没有实质性的上下文差异时(如此处),最佳解决方案应至少具有与特定问题的解决方案相同的轮廓。

–地理理论
2015年9月4日15:47

我认为上面的sed答案证明了Perl的答案是话题。

– reinierpost
2015年11月24日10:46



稍微容易一点:使用“ -p0e”,不需要“ -0777”。 unix.stackexchange.com/a/181215/197502

–魏登琳德
17 Mar 3 '17 at 11:59

#3 楼

我认为,最好将\n符号替换为其他符号,然后照常工作:

例如无法正常工作的源代码:

cat alpha.txt | sed -e 's/a test\nPlease do not/not a test\nBe/'


可以更改为:

cat alpha.txt | tr '\n' '\r' | sed -e 's/a test\rPlease do not/not a test\rBe/'  | tr '\r' '\n'


如果有人不这样做知道,\n是UNIX行尾,\r\n-Windows,\r-经典Mac OS。普通的UNIX文本不使用\r符号,因此在这种情况下使用它是安全的。

还可以使用一些奇异符号临时替换\ n。例如-\ f(换页符号)。您可以在此处找到更多符号。

cat alpha.txt | tr '\n' '\f' | sed -e 's/a test\fPlease do not/not a test\fBe/'  | tr '\f' '\n'


评论


为这个聪明的hack +1!除非您完全确定要编辑的文件的内容,否则有关使用外来符号临时替换换行符的建议尤其有用。

– L0j1k
2015年2月6日在21:26

这不能像在OS X上那样工作。相反,需要用$(printf'\ r')替换sed参数中所有\ r实例。

– abeboparebop
16年2月11日在15:33

@abeboparebop:很棒的发现! 👍或者,使用自制软件安装GNU sed:stackoverflow.com/a/30005262

–ssc
17年1月24日在20:05

@abeboparebop,在OSX上,您只需要在sed字符串之前添加$,以防止其将\ r转换为r。简短示例:sed $'s / \ r /〜/'。完整示例:cat alpha.txt | tr'\ n''\ r'| sed $'s / a test \ r请不要/不是测试\ rBe /'| tr'\ r''\ n'

– Wisbucky
19年8月5日在21:49

#4 楼

考虑所有因素,将整个文件吞噬可能是最快的方法。基本语法如下:

sed -e '1h;2,$H;$!d;g' -e 's/__YOUR_REGEX_GOES_HERE__...'


打扰您,如果文件过大,则无法选择使整个文件不可靠。对于此类情况,此处提供的其他答案提供了可确保在较小内存占用空间上工作的定制解决方案。

对于所有其他破解和斜线情况,只需在-e '1h;2,$H;$!d;g'前面加上原始的sed正则表达式参数即可完成工作。

例如

$ echo -e "Dog\nFox\nCat\nSnake\n" | sed -e '1h;2,$H;$!d;g' -re 's/([^\n]*)\n([^\n]*)\n/Quick \nLazy \n/g'
Quick Fox
Lazy Dog
Quick Snake
Lazy Cat


-e '1h;2,$H;$!d;g'的作用是什么?

12,$$!部分是行说明符,用于限制直接执行以下命令的行。



1:仅第一行

2,$:所有行从第二个

$!:除最后一行以外的每一行

如此扩展,这就是N行输入的每一行上发生的情况。

  1: h, d
  2: H, d
  3: H, d
  .
  .
N-2: H, d
N-1: H, d
  N: H, g


g命令未指定行指定符,但前面的d命令具有特殊的子句“启动下一个周期。”,这可防止g在除最后一行以外的所有行上运行。 >
关于每个命令的含义:


每行的第一个h和后跟的H会将输入的这些行复制到sed的保持空间中。 (请考虑使用任意文本缓冲区。)
然后,d会丢弃每一行,以防止将这些行写入输出。但是,保留空间被保留。
最后,在最后一行,g恢复了保留空间中每行的累积,以便sed能够在整个输入上运行其正则表达式(而不是在行中-一次),因此能够与\n s匹配。


评论


如果添加了-i开关,这也可以修改现有文件(perl答案称为“就地替换”)

–textral
2月3日5:49

/!\别忘了在替换命令的末尾添加g:s /.../.../ g-当然,除非您只想更改文件的第一个匹配项。

– Mathieu CAROFF
5月15日13:36

#5 楼

sed具有三个命令来管理多行操作:NDP(将它们与普通ndp进行比较)。在这种情况下,您可以匹配模式的第一行,使用N将第二行添加到模式空间,然后使用s进行替换。

类似的东西:

/a test$/{
  N
  s/a test\nPlease do not/not a test\nBe/
}


评论


这太棒了!比接受的答案简单,并且仍然有效。

–jeyk
15年11月21日在20:35

以及所有涉及容纳空间的(G,H,x ...)。也可以使用s命令将更多行添加到模式空间中。

–StéphaneChazelas
16年8月11日在6:38

添加了到sed命令参考的链接,单个unix规范v2,1997。

–n611x007
16-12-12在9:12



此解决方案不适用于以下情况:“这是\ n测试\ n测试\ n请不要\ n发出警报”

–mug896
17年1月14日在15:13



@ mug896您很可能需要多个N命令

–loa_in_
18年5月24日在8:30

#6 楼

GNU sed具有-z选项,该选项允许使用OP尝试应用的语法。 (手册页)

示例:

$ cat alpha.txt
This is
a test
Please do not
be alarmed


$ sed -z 's/a test\nPlease do not\nbe/not a test\nBe/' -i alpha.txt


$ cat alpha.txt
This is
not a test
Be alarmed


是注意:如果使用^$,它们现在将匹配以NUL字符(不是\n)分隔的行的开头和结尾。并且,要确保替换所有(\n分隔的)行上的匹配项,请不要忘记使用g标志进行全局替换(例如s/.../.../g)。 -chazelas在上面的评论中首先提到了-z。

评论


好答案。适用于整个文件。

– Michael S
8月3日12:18

比接受的答案简单得多,而且行得通。

–安德鲁·菲尔登(Andrew Fielden)
12月8日,11:27

#7 楼

您可以,但是很难。我建议切换到其他工具。如果有一个正则表达式与要替换的文本的任何部分都不匹配,则可以将其用作GNU awk中的awk记录分隔符。

awk -v RS='a' '{gsub(/hello/, "world"); print}'


如果存在永远不会是搜索字符串中连续两个换行符,可以使用awk的“段落模式”(一个或多个空行分隔记录)。

awk -v RS='' '{gsub(/hello/, "world"); print}'


一个简单的解决方案是使用Perl并将文件完全加载到内存中。

perl -0777 -pe 's/hello/world/g'


评论


如何将perl命令应用于文件?

–sebix
16年2月2日在13:34

@sebix perl -0777 -pe'...'<输入文件>输出文件。要修改文件,请使用perl -0777 -i -pe'…'文件名

–吉尔斯'所以-不再是邪恶的'
16-2-2在13:56

另请参见GNU sed的-z选项(发布该答案后于2012年添加):seq 10 | sed -z's / 4 \ n5 / a \ nb /'。

–StéphaneChazelas
16年8月11日在6:42

#8 楼

我认为这是2行匹配的sed解决方案。

sed -n '$!N;s@a test\nPlease do not@not a test\nBe@;P;D' alpha.txt


如果要3行匹配,那么...

sed -n '1{$!N};$!N;s@aaa\nbbb\nccc@xxx\nyyy\nzzz@;P;D'


如果要匹配4条线,则...

sed -n '1{$!N;$!N};$!N;s@ ... @ ... @;P;D'


如果“ s”命令中的替换零件收缩线
,则像这样稍微复杂一点

# aaa\nbbb\nccc shrink to one line "xxx"

sed -n '1{$!N};$!N;/aaa\nbbb\nccc/{s@@xxx@;$!N;$!N};P;D'


如果重新安置部分长了线,那么像这样稍微复杂一点br />
第二种方法是一种简单的复制和逐字记录替换,通常替换为小型文本文件(需要shell脚本文件)。

# aaa\nbbb\nccc grow to five lines vvv\nwww\nxxx\nyyy\nzzz

sed -n '1{$!N};$!N;/aaa\nbbb\nccc/{s@@vvv\nwww\nxxx\nyyy\nzzz@;P;s/.*\n//M;P;s/.*\n//M};P;D'


评论


这应该可以达到顶峰!我只是在两行替换中使用了“ -i”而不是“ -n”,因为这是我所需要的,顺便说一句,这也在asker的示例中。

– Nagev
18年5月15日在13:22

#9 楼

sed -i'.original' '/a test/,/Please do not/c not a test \nBe' alpha.txt


这里/a test/,/Please do not/被视为(多行)文本的块,c是更改命令,后跟新文本not a test \nBe

如果要替换的文本很长,我建议使用ex语法。

评论


哎呀,问题是sed会替换/ a test /和/请不要/之间的所有最终文本... :(

– noonex
16年11月24日在8:47

#10 楼

 sed -e'$!N;s/^\(a test\n\)Please do not be$/not Be/;P;D' <in >out
 


只需稍微扩大输入范围即可。

这很容易。除了标准替代;您只需要在这里$!NPD

#11 楼

除了Perl之外,用于流(和文件)的多行编辑的一种通用且方便的方法是:

首先根据需要创建一些新的UNIQUE行分隔符,例如

$ S=__ABC__                     # simple
$ S=__$RANDOM$RANDOM$RANDOM__   # better
$ S=$(openssl rand -hex 16)     # ultimate


然后在您的sed命令(或任何其他工具)中,用$ {S}替换\ n,例如

$ cat file.txt | awk 1 ORS=$S |  sed -e "s/a test${S}Please do not/not a test\nBe/" | awk 1 RS=$S > file_new.txt


(awk替换为ASCII行分隔符,反之亦然。)

评论


不要为设计为确定性的过程使用随机值。它们将使您的解决方案在与随机值匹配的极端情况下随机失败,并且您将很难重现该问题(因为它是随机产生的)。请使用针对该问题设计的命令。 sed -z将是一个解决方案,因为文本流通常不包含NUL字符。

– Peterino
8月4日12:59



#12 楼

这是对xara的聪明回答的一个小修改,使其可以在OS X上运行(我使用的是10.10):

cat alpha.txt | tr '\n' '\r' | sed -e 's/a test$(printf '\r')Please do not/not a test$(printf '\r')Be/'  | tr '\r' '\n'


而不是显式使用\r,您必须使用$(printf '\r')

评论


尽管printf'\ r'(或echo -e'\ r')可以正常工作,但是请注意,您可以只使用shell语法$'\ r'来引用转义的文字。例如,echo hi $'\ n'将在hi和there之间回显换行符。同样,您可以包装整个字符串,以便每个反斜杠\将转义其后续字符:echo $'hi \ nthere'

– Dejay Clayton
18-2-28在1:40



#13 楼

我想使用sed向文件中添加几行HTML(并在这里结束)。通常我只使用perl,但我当时使用的是sed,bash和其他功能。我发现,如果我将字符串更改为一行,然后让bash / sed插值\ t \ n一切都成功了:

HTML_FILE='a.html' #contains an anchor in the form <a name="nchor" />
BASH_STRING_A='apples'
BASH_STRING_B='bananas'
INSERT="\t<li>$BASH_STRING_A<\/li>\n\t<li>$BASH_STRING_B<\/li>\n<a name=\"nchor\"\/>"
sed -i "s/<a name=\"nchor"\/>/$INSERT/" $HTML_FILE


用于避免双引号和正斜杠的函数,但是有时抽象是时间的窃贼。

#14 楼

Sed在换行符上中断输入。每个循环只保留一行。
因此,如果模式空间不包含\n(换行符),则无法匹配它。

有一种方法可以解决,可以使用循环使sed在模式空间中保持连续的两行:

sed 'N;l;P;D' alpha.txt


在N和P之间添加所需的任何处理(替换l)。 br />
在这种情况下(2行):

$ sed 'N;s/a test\nPlease do not/not a test\nBe/;P;D' alpha.txt
This is
not a test
Be
be alarmed


或者,对于三行:

$ sed -n '1{$!N};$!N;s@a test\nPlease do not\nbe@not a test\nDo\nBe@;P;D' alpha.txt 
This is
not a test
Do
Be alarmed


假设替换了相同数量的行。

#15 楼

尽管ripgrep特别不支持内联替换,但我发现其当前的--replace功能已在该用例中使用,并且比使用sed更可取,例如:
“> rg --replace $'not a test\nBe' --passthru --no-line-number \ --multiline 'a test\nPlease do not' alpha.txt > output.txt
说明:


--replace 'string'启用替换模式并设置替换字符串。可以通过使用等包括捕获的正则表达式组。

$'string'是Bash扩展,因此\n成为多行字符串的换行符。与正则表达式模式匹配的行。使用此选项,它还会显示文件中所有不匹配的行。

--passthru是因为默认情况下,ripgrep在输出中包括行号(仅在显示匹配行时有用)。 br />
--no-line-number / -N启用默认情况下禁用的多行处理。

ripgrep,带有--multiline / -U> output.txt选项,标准输出与期望的新文件匹配,但有替换项,并且可以照常保存。

如果希望点('。')正则表达式模式与换行符(--passthrough)相匹配,则可以选择添加no-line-number

但是,此命令对于处理多个文件,因为每个文件需要单独运行。

#16 楼

既然这已经解释了大多数sed操作,那么我将添加如何在块内搜索。
您想在x内更改padding而不是offset:从padding: {}
{
  padding: {
    x: 2,
    y: 0
  },
  offset: {
    x: 0,
    y: 1
  }
}

尽管它不如JSON示例那么优雅,但这也可以回答这个问题:
sed -r '/padding: \{/,/\}/ {
    # and inside the block you replace the value of x:
    s/^( +x:).*/ 1,/
}'