Bow*_*ark 10 grep sed text-processing newlines
使用bash外壳,在具有如下行的文件中
first "line"
<second>line and so on
Run Code Online (Sandbox Code Playgroud)
我想每次都替换一次或多次出现的"line"\n<second>withother characters并获得:
first other characters line and so on
Run Code Online (Sandbox Code Playgroud)
所以我必须用特殊字符(例如"和 )<和换行符替换字符串。
在其他答案之间搜索后,我发现sed可以接受命令右侧的换行符(因此,other characters字符串),但不能接受左侧的换行符。
有没有办法(比这更简单)用sed或获得这个结果grep?
好吧,我可以想到几种简单的方法,但都不涉及grep(无论如何都不会进行替换)或sed.
珀尔
要更换每个发生"line"\n<second>用other characters,使用:
$ perl -00pe 's/"line"\n<second>/other characters /g' file
first other characters line and so on
Run Code Online (Sandbox Code Playgroud)
或者,要将多个连续出现的"line"\n<second>视为一个,并将它们全部替换为单个other characters,请使用:
perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
Run Code Online (Sandbox Code Playgroud)
例子:
$ 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
Run Code Online (Sandbox Code Playgroud)
将-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
Run Code Online (Sandbox Code Playgroud)
同样的基本思想,我们将记录分隔符(RS)设置\n\n为 slurp 整个文件,然后将输出记录分隔符设置为空(否则会打印额外的换行符),然后使用该sub()函数进行替换。
读取整个文件并进行全局替换:
sed -n 'H; ${x; s/"line"\n<second>/other characters /g; p}' <<END
first "line"
<second> line followed by "line"
<second> and last
END
Run Code Online (Sandbox Code Playgroud)
first other characters line followed by other characters and last
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)
它们三个都建立在基本的s///替换命令之上:
s/"[^"]*"\n<[^>]*>/other characters /
Run Code Online (Sandbox Code Playgroud)
他们还尝试小心处理最后一行,因为sed在边缘情况下其输出往往会有所不同。这就是它的含义是匹配不是最后$!一行的每一行的地址。!$
它们还都使用Next 命令将下一个输入行附加到模式空间中的\newline 字符后面。任何已经sed使用了一段时间的人都会学会依赖\newline 字符 - 因为获得该字符的唯一方法就是明确地将其放在那里。
所有这三个都在采取行动之前尝试读入尽可能少的输入 -sed尽可能快地采取行动,并且在此之前不需要读入整个输入文件。
尽管它们都做了N,但它们三者的递归方法都不同。
第一个命令使用一个非常简单的N;P;D循环。这三个命令内置于任何 POSIX 兼容的命令中sed,并且它们可以很好地相互补充。
N- 如前所述,将Next 输入行附加到插入的 ewline 分隔符之后的模式空间\n。P- 喜欢p; 它P打印模式空间 - 但仅限于第一个出现的\newline 字符。因此,给出以下输入/命令:
printf %s\\n one two | sed '$!N;P;d'sed P仅打印一个. 然而,随着...
D- 喜欢d; 它D删除模式空间并开始另一个行循环。与 不同的是 d,D仅删除\n模式空间中第一个出现的 ewline。\n如果ewline 字符后面的模式空间中有更多内容,sed则使用剩余的内容开始下一个行循环。例如,如果d将上一个示例中的 替换为 a ,则将同时打印one和Two。DsedP此命令仅对与ubstitution 语句不匹配的行进行递归s///。因为s///ubstitution 删除了\n添加的 ewline ,所以当eletes 模式空间N时永远不会剩下任何东西。sed D
可以进行测试来选择性地应用P和/或D,但还有其他更适合该策略的命令。由于实现递归是为了处理仅与替换规则的一部分匹配的连续行,因此与替换两端匹配的连续行序列s///效果不佳:
鉴于此输入:
first "line"
<second>"line"
<second>"line"
<second>line and so on
Run Code Online (Sandbox Code Playgroud)
...它打印...
first other characters "line"
<second>other characters line and so on
Run Code Online (Sandbox Code Playgroud)
然而,它确实处理
first "line"
second "line"
<second>line
Run Code Online (Sandbox Code Playgroud)
...正好。
该命令与第三个命令非常相似。两者都采用:branch/ test 标签(正如 Joeseph R. 的回答中所演示的那样),并在给定某些条件的情况下递归回它。
-e :n -e- 可移植脚本将使用ewline 或新的内联执行语句来sed分隔标签定义。
:\n-e:n- 定义一个名为 的标签n。可以随时使用 或 返回此bn内容tn。tn- test 命令返回到指定的标签(或者,如果没有提供,则退出当前行周期的脚本),如果s///自定义标签或自上次调用tests 以来有任何替换成功。在此命令中,对匹配行进行递归。如果成功用其他字符sed替换模式,则返回标签并重试。如果未执行替换,则会自动打印模式空间并开始下一个行周期。sed:ns///sed
这往往可以更好地处理连续序列。如果最后一个失败,则打印:
first other characters other characters other characters line and so on
Run Code Online (Sandbox Code Playgroud)
如前所述,这里的逻辑与上一个非常相似,但测试更加明确。
/"$/bn- 这是sed测试。因为branch 命令是该地址的函数,所以只有在附加 ewline 且模式空间仍以双引号结束后sed才会branch 返回。:n\n"N在和 之间尽可能少地完成b- 通过这种方式sed可以非常快速地收集所需的精确输入,以确保下一行无法匹配您的规则。替换s///的不同之处在于它使用g全局标志 - 因此它将立即进行所有必要的替换。给定相同的输入,此命令的输出与上一个命令相同。