我注意到,如果我添加\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
我该如何工作?
我注意到,如果我添加\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
我该如何工作?
Answers:
在最简单的调用sed的,它有一个模式空间,即文本行。\n
输入的1行分隔文本。模式空间中的单行没有\n
...这就是您的正则表达式找不到任何内容的原因。
您可以将多行读入模式空间,并且可以出乎意料地很好地进行操作,但是要比平常付出更多的努力。.Sed具有一组允许这种类型的命令的命令。。这是我找到的最好的,并且让我滚动。
但是,一旦开始使用sed的微命令,请不要忘记“单线”想法。将其像结构化程序一样进行布局是很有用的,直到您获得它的感觉为止。它非常简单,而且同样不寻常。您可以将其视为文本编辑的“汇编语言”。
简介:将sed用于简单的事情,甚至可能更多,但是总的来说,当它超出了单行的工作范围时,大多数人会喜欢其他东西……
我会让别人建议其他东西。真的不确定最好的选择是什么(我会使用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
t
此处使用该命令-当未提供标签时,默认情况下将跳转到脚本末尾。所以sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;t;P;D}}' alpha.txt
不完全一样,在任何情况下你的命令。当然,对于此特定文件,sed '/test/{N;s/.*/not a test\nBe/}' alpha.txt
也执行相同的操作,但是我的第一个示例在逻辑上等效于所有可能的文件。另请注意,\n
在替换字符串中不会产生换行符;您需要反斜杠`\`,然后再加上实际的换行符来执行此操作。
#
命令与\n
RHS中的上一个命令没有分开s
)。使用GNU,sed
您还-z
可以使用NUL分隔的记录(然后将整个输入内容(如果为文本)(根据定义,其中不包含NUL)添加进来)。
使用perl
代替sed
:
$ perl -0777 -i.original -pe 's/a test\nPlease do not/not a test\nBe/igs' alpha.txt
$ diff alpha.txt{,.original}
2,3c2,3
< not a test
< Be
---
> a test
> Please do not
-pi -e
是您的标准“就地替换”命令行序列,并且-0777使perl吞噬整个文件。有关更多信息,请参见perldoc perlrun。
sed
使用awk或perl 进行答复并出现是很常见的。我认为这不是主题,因此,抱歉,我开了一分。
sed
以上答案证明了Perl的答案是话题。
我认为,最好\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
-窗口\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'
\r
的参数sed
用$(printf '\r')
。
$
在sed字符串之前添加,以防止将其转换\r
为r
。简短示例:sed $'s/\r/~/'
。完整示例:cat alpha.txt | tr '\n' '\r' | sed $'s/a test\rPlease do not/not a test\rBe/' | tr '\r' '\n'
考虑所有因素,将整个文件吞噬可能是最快的方法。
基本语法如下:
sed -e '1h;2,$H;$!d;g' -e 's/__YOUR_REGEX_GOES_HERE__...'
请注意,如果文件很大,则无法选择将整个文件都吞噬。对于这种情况,此处提供的其他答案提供了定制的解决方案,这些解决方案可保证在较小的内存空间上工作。
对于所有其他破解和斜线情况,只需在前面-e '1h;2,$H;$!d;g'
加上原始sed
regex参数即可完成工作。
例如
$ echo -e "Dog\nFox\nCat\nSnake\n" | sed -e '1h;2,$H;$!d;g' -re 's/([^\n]*)\n([^\n]*)\n/Quick \2\nLazy \1\n/g'
Quick Fox
Lazy Dog
Quick Snake
Lazy Cat
-e '1h;2,$H;$!d;g'
办?的1
,2,$
,$!
部分是行限定符的直接下面的命令运行在其上线该限制。
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
命令有一个特殊的子句“ Start next cycle。 ”,这阻止g
了除最后一行之外的所有行上运行。
至于每个命令的含义:
h
,接着H
在每行拷贝据说输入的线成sed
的保留空间。(请考虑任意文本缓冲区。)d
丢弃每一行以防止这些行写入输出。在保持空间却得以保留。g
从保持空间恢复每行的累积,以便sed
能够在整个输入上运行其正则表达式(而不是以一次一行的方式),因此能够在上匹配\n
。sed
有三个命令来管理多线作战:N
,D
和P
(他们比较正常 n
,d
和p
)。
在这种情况下,您可以匹配模式的第一行,用于N
将第二行追加到模式空间,然后用于s
进行替换。
就像是:
/a test$/{
N
s/a test\nPlease do not/not a test\nBe/
}
G
,H
,x
...)。也可以使用该s
命令在模式空间中添加更多行。
N
命令
您可以,但是很难。我建议切换到其他工具。如果有一个正则表达式与要替换的文本的任何部分都不匹配,则可以将其用作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 -0777 -pe '…' <input-file >output-file
。要修改文件,perl -0777 -i -pe '…' filename
sed
的-z
选项(在2012年加入这个答案被张贴后): seq 10 | sed -z 's/4\n5/a\nb/'
。
我认为这是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'
如果退役部分长出线,则这样会更复杂
# 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'
除了Perl之外,用于流(和文件)的多行编辑的一种通用且方便的方法是:
首先,根据需要创建一些新的UNIQUE行分隔符
$ S=__ABC__ # simple
$ S=__$RANDOM$RANDOM$RANDOM__ # better
$ S=$(openssl rand -hex 16) # ultimate
然后在sed命令(或任何其他工具)中,将\ n替换为$ {S},例如
$ 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行分隔符,反之亦然。)
这是对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'there
将在hi
和之间回显换行符there
。同样,您可以包装整个字符串,以便每个反斜杠\
都可以转义其后续字符:echo $'hi\nthere'
我想使用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
具有转义双引号和正斜杠的功能会更清洁,但有时抽象是时间的窃贼。
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
)。
鸣谢: @stéphane-chazelas 在上面的评论中首先提到了-z。
Sed在换行符上中断输入。每个循环仅保留一行。
因此,\n
如果模式空间不包含(换行符),则无法匹配它。
但是,有一种方法可以使sed 通过使用循环在模式空间中保留两个连续的行:
sed 'N;l;P;D' alpha.txt
在N和P之间添加所需的所有处理(替换l
)。
在这种情况下(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
假设替换了相同数量的行。