使用bash
shell,在包含如下行的文件中
first "line"
<second>line and so on
我想将出现的一个或多个替换"line"\n<second>
为other characters
并每次都获取:
first other characters line and so on
"
所以我必须用特殊字符(例如和)替换字符串并<
用换行符替换。
在其他答案之间搜索后,我发现sed
可以在命令的右侧接受换行符(因此,字符串other characters
),但不能在左侧接受换行符。
有没有办法(比这)用sed
或来获得这个结果grep
?
答案1
好吧,我可以想到一些简单的方法,但都不涉及grep
(无论如何都不进行替换)或sed
.
Perl
取代每个出现
"line"\n<second>
withother characters
,使用:$ perl -00pe 's/"line"\n<second>/other characters /g' file first other characters line and so on
或者,要将多个连续出现的 视为
"line"\n<second>
一个,并将它们全部替换为单个other characters
,请使用:perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
例子:
$ cat file first "line" <second>"line" <second>"line" <second>line and so on $ perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file first other characters line and so on
这
-00
导致 Perl 以“段落模式”读取文件,这意味着“行”是由\n\n
而不是定义的\n
,本质上,每个段落都被视为一行。因此,替换会跨越换行符进行匹配。awk
$ awk -v RS="\n\n" -v ORS="" '{ sub(/"line"\n<second>/,"other characters ", $0) print; }' file first other characters line and so on
基本思想相同,我们将记录分隔符 (
RS
) 设置为\n\n
slurp 整个文件,然后将输出记录分隔符设置为空(否则会打印额外的换行符),然后使用该sub()
函数进行替换。
答案2
读取整个文件并进行全局替换:
sed -n 'H; ${x; s/"line"\n<second>/other characters /g; p}' <<END
first "line"
<second> line followed by "line"
<second> and last
END
first other characters line followed by other characters and last
答案3
三个不同的sed
命令:
sed '$!N;s/"[^"]*"\n<[^>]*>/other characters /;P;D'
sed -e :n -e '$!N;s/"[^"]*"\n<[^>]*>/other characters /;tn'
sed -e :n -e '$!N;/"$/{$!bn' -e '};s/"[^"]*"\n<[^>]*>/other characters /g'
它们三个都建立在基本的s///
替换命令之上:
s/"[^"]*"\n<[^>]*>/other characters /
他们还尝试小心处理最后一行,因为sed
在边缘情况下其输出往往会有所不同。这就是它的含义是匹配不是最后$!
一行的每一行的地址。!
$
它们还都使用N
ext 命令将下一个输入行附加到模式空间中的\n
ewline 字符后面。任何已经sed
使用了一段时间的人都会学会依赖\n
ewline 字符 - 因为获得该字符的唯一方法就是明确地将其放在那里。
所有这三个都在采取行动之前尝试读入尽可能少的输入 -sed
尽可能快地采取行动,并且在此之前不需要读入整个输入文件。
尽管它们都做了N
,但它们三者的递归方法都不同。
第一个命令
第一个命令使用一个非常简单的N;P;D
循环。这三个命令内置于任何 POSIX 兼容的命令中,sed
并且它们可以很好地相互补充。
N
- 如前所述,将 ext 输入行附加到插入的ewline 分隔N
符之后的模式空间。\n
P
- 喜欢p
;它P
打印模式空间 - 但仅限于第一个出现的\n
ewline 字符。因此,给出以下输入/命令:printf %s\\n one two | sed '$!N;P;d'
sed
P
仅打印一。然而,随着...D
- 喜欢d
;它D
删除模式空间并开始另一个行循环。不像d
,D
仅删除\n
模式空间中第一个出现的 ewline。如果\n
ewline 字符后面的模式空间中有更多内容,sed
则使用剩余的内容开始下一个行循环。例如,如果将d
前面示例中的 替换为 a ,则将同时打印D
sed
P
一和二。
该命令仅对以下行进行递归不要匹配s///
替换语句。因为s///
ubstitution 删除了\n
添加的 ewline ,所以当eletes 模式空间N
时永远不会剩下任何东西。sed
D
可以进行测试来选择性地应用P
和/或D
,但还有其他更适合该策略的命令。因为递归是为了处理仅匹配的连续行而实现的部分替换规则的连续行序列匹配两端替代s///
品效果不佳:
鉴于此输入:
first "line"
<second>"line"
<second>"line"
<second>line and so on
...它打印...
first other characters "line"
<second>other characters line and so on
然而,它确实可以处理
first "line"
second "line"
<second>line
...正好。
第二条命令
该命令与第三个命令非常相似。两者都采用:b
牧场/ t
est 标签(Jooseph R. 的回答也证明了这一点这里)并在给定某些条件的情况下递归回它。
-e :n -e
- 可移植脚本将使用ewline 或新的内联执行语句sed
来分隔标签定义。:
\n
-e
:n
- 定义一个名为 的标签n
。可以随时使用bn
或返回此内容tn
。
tn
-t
est命令返回指定标签(或者,如果未提供,则退出当前行周期的脚本)如果s///
自定义标签或自上次调用以来有任何替换,则认为t
是成功的。
在此命令中,对匹配行进行递归。如果sed
成功地将模式替换为其他角色,sed
返回:n
标签并重试。如果s///
未执行替换,则会sed
自动打印模式空间并开始下一个行周期。
这往往可以更好地处理连续序列。如果最后一个失败,则打印:
first other characters other characters other characters line and so on
第三条命令
如前所述,这里的逻辑与上一个非常相似,但测试更加明确。
/"$/bn
- 这是sed
测试。因为b
ranch 命令是该地址的函数,所以只有在附加 ewline 且模式空间仍以双引号结尾后sed
才会b
ranch 返回。:n
\n
"
N
在和之间尽可能少地完成b
- 通过这种方式sed
可以非常快速地收集所需的精确输入,以确保下一行无法匹配您的规则。替换的s///
不同之处在于它使用g
全局标志 - 因此它将立即进行所有必要的替换。给定相同的输入,该命令的输出与上一个命令相同。
答案4
这是一个变体格伦的回答如果您有多个连续出现的情况,这将起作用(sed
仅适用于 GNU):
sed ':x /"line"/N;s/"line"\n<second>/other characters/;/"line"/bx' your_file
这:x
只是分支的标签。基本上,它的作用是检查替换后的行,如果它仍然匹配"line"
,它会分支回:x
标签(这就是所做的bx
)并将另一行添加到缓冲区并开始处理它。