Bel*_*dez 353 sed utilities regular-expression
我注意到,如果我添加\n到一个模式来替换 using 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
Run Code Online (Sandbox Code Playgroud)
我怎样才能让它发挥作用?
Pet*_*r.O 306
在最简单的调用sed的,它有一个模式空间,即文本行。\n输入中的1 行分隔文本。模式空间中的单行没有\n......这就是你的正则表达式没有找到任何东西的原因。
您可以将多行读入模式空间并以惊人的方式操纵事物,但需要付出比平常更多的努力.. Sed 有一组命令允许这种类型的事情......这是一个指向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
Run Code Online (Sandbox Code Playgroud)
这是相同的脚本,浓缩成明显更难阅读和使用的内容,但有些人会怀疑地称之为单行
sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt
Run Code Online (Sandbox Code Playgroud)
这是我的命令“备忘单”
: # 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
Run Code Online (Sandbox Code Playgroud)
cod*_*ead 243
使用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
Run Code Online (Sandbox Code Playgroud)
-pi -e是标准的“就地替换”命令行序列,-0777 会导致 perl 将文件全部吞掉。请参阅perldoc perlrun以了解有关它的更多信息。
小智 139
我认为,最好\n用其他符号替换符号,然后照常工作:
例如未工作的源代码:
cat alpha.txt | sed -e 's/a test\nPlease do not/not a test\nBe/'
Run Code Online (Sandbox Code Playgroud)
可以改为:
cat alpha.txt | tr '\n' '\r' | sed -e 's/a test\rPlease do not/not a test\rBe/' | tr '\r' '\n'
Run Code Online (Sandbox Code Playgroud)
如果有人不知道,\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'
Run Code Online (Sandbox Code Playgroud)
ant*_*tak 66
考虑到所有因素,吞噬整个文件可能是最快的方法。
基本语法如下:
sed -e '1h;2,$H;$!d;g' -e 's/__YOUR_REGEX_GOES_HERE__...'
Run Code Online (Sandbox Code Playgroud)
请注意,如果文件非常大,吞食整个文件可能不是一种选择。对于这种情况,此处提供的其他答案提供了定制的解决方案,保证在较小的内存占用上工作。
对于所有其他 hack 和 slash 情况,仅在前面-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 \2\nLazy \1\n/g'
Quick Fox
Lazy Dog
Quick Snake
Lazy Cat
Run Code Online (Sandbox Code Playgroud)
-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
Run Code Online (Sandbox Code Playgroud)
该g命令没有给出行说明符,但前面的d命令有一个特殊的子句“ Start next cycle. ”,这可以防止g在除最后一行之外的所有行上运行。
至于每个命令的含义:
h后跟Hs 将所述输入行复制到sed的保留空间中。(想想任意文本缓冲区。)d丢弃每一行以防止这些行被写入输出。在保持空间却得以保留。g从保持空间恢复每一行的累积,以便sed能够在整个输入上运行其正则表达式(而不是一次一行的方式),因此能够匹配\ns。and*_*coz 48
sed有三个命令来管理多行操作:N,D和P(将它们与普通的 n,d和进行比较p)。
在这种情况下,您可以匹配模式的第一行,用于N将第二行附加到模式空间,然后用于s进行替换。
就像是:
/a test$/{
N
s/a test\nPlease do not/not a test\nBe/
}
Run Code Online (Sandbox Code Playgroud)
Pet*_*ino 42
GNUsed有一个-z选项,允许使用 OP 尝试应用的语法。(手册页)
例子:
$ cat alpha.txt
This is
a test
Please do not
be alarmed
Run Code Online (Sandbox Code Playgroud)
$ sed -z 's/a test\nPlease do not\nbe/not a test\nBe/' -i alpha.txt
Run Code Online (Sandbox Code Playgroud)
$ cat alpha.txt
This is
not a test
Be alarmed
Run Code Online (Sandbox Code Playgroud)
请注意:如果您使用^and$它们现在匹配以 NUL 字符(不是\n)分隔的行的开头和结尾。并且,为了确保\n替换所有(-separated)行上的匹配项,不要忘记使用g全局替换标志(例如s/.../.../g)。
致谢: @stéphane-chazelas在上面的评论中首先提到了 -z。
Gil*_*il' 17
你可以,但很难。我建议切换到不同的工具。如果有一个正则表达式永远不会匹配您要替换的文本的任何部分,您可以将其用作 GNU awk 中的 awk 记录分隔符。
awk -v RS='a' '{gsub(/hello/, "world"); print}'
Run Code Online (Sandbox Code Playgroud)
如果您的搜索字符串中从来没有两个连续的换行符,您可以使用 awk 的“段落模式”(一个或多个空白行分隔记录)。
awk -v RS='' '{gsub(/hello/, "world"); print}'
Run Code Online (Sandbox Code Playgroud)
一个简单的解决方案是使用 Perl 并将文件完全加载到内存中。
perl -0777 -pe 's/hello/world/g'
Run Code Online (Sandbox Code Playgroud)
mug*_*896 11
我认为这是 2 行匹配的 sed 解决方案。
sed -n '$!N;s@a test\nPlease do not@not a test\nBe@;P;D' alpha.txt
Run Code Online (Sandbox Code Playgroud)
如果你想要 3 行匹配,那么......
sed -n '1{$!N};$!N;s@aaa\nbbb\nccc@xxx\nyyy\nzzz@;P;D'
Run Code Online (Sandbox Code Playgroud)
如果你想要 4 行匹配,那么......
sed -n '1{$!N;$!N};$!N;s@ ... @ ... @;P;D'
Run Code Online (Sandbox Code Playgroud)
如果“s”命令中的替换部分缩小了行,那么像这样更复杂一点
# aaa\nbbb\nccc shrink to one line "xxx"
sed -n '1{$!N};$!N;/aaa\nbbb\nccc/{s@@xxx@;$!N;$!N};P;D'
Run Code Online (Sandbox Code Playgroud)
如果更换部分长出线,那么像这样更复杂一点
# 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'
Run Code Online (Sandbox Code Playgroud)
第二种方法是对通常的小型文本文件(需要一个 shell 脚本文件)进行简单的逐字复制和粘贴替换
#!/bin/bash
# copy & paste content that you want to substitute
AA=$( cat <<\EOF | sed -z -e 's#\([][^$*\.#]\)#\\\1#g' -e 's#\n#\\n#g'
a test
Please do not
EOF
)
BB=$( cat <<\EOF | sed -z -e 's#\([&\#]\)#\\\1#g' -e 's#\n#\\n#g'
not a test
Be
EOF
)
sed -z -i 's#'"${AA}"'#'"${BB}"'#g' *.txt # apply to all *.txt files
Run Code Online (Sandbox Code Playgroud)
sed -i'.original' '/a test/,/Please do not/c not a test \nBe' alpha.txt
Run Code Online (Sandbox Code Playgroud)
这里/a test/,/Please do not/被视为(多行)文本块,c是更改命令后跟新文本not a test \nBe
如果要替换的文本很长,我建议使用ex语法。
小智 5
除了 Perl,用于流(和文件)的多行编辑的通用且方便的方法是:
例如,首先根据需要创建一些新的 UNIQUE 行分隔符
$ S=__ABC__ # simple
$ S=__$RANDOM$RANDOM$RANDOM__ # better
$ S=$(openssl rand -hex 16) # ultimate
Run Code Online (Sandbox Code Playgroud)
然后在您的 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
Run Code Online (Sandbox Code Playgroud)
( awk 用你的替换 ASCII 行分隔符,反之亦然。)