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
在边缘情况下其输出往往会有所不同。这就是它的含义是匹配不是最后$!
一行的每一行的地址。!
$
它们还都使用N
ext 命令将下一个输入行附加到模式空间中的\n
ewline 字符后面。任何已经sed
使用了一段时间的人都会学会依赖\n
ewline 字符 - 因为获得该字符的唯一方法就是明确地将其放在那里。
所有这三个都在采取行动之前尝试读入尽可能少的输入 -sed
尽可能快地采取行动,并且在此之前不需要读入整个输入文件。
尽管它们都做了N
,但它们三者的递归方法都不同。
第一个命令使用一个非常简单的N;P;D
循环。这三个命令内置于任何 POSIX 兼容的命令中sed
,并且它们可以很好地相互补充。
N
- 如前所述,将N
ext 输入行附加到插入的 ewline 分隔符之后的模式空间\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 ,则将同时打印one和Two。D
sed
P
此命令仅对与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)
...正好。
该命令与第三个命令非常相似。两者都采用:b
ranch/ t
est 标签(正如 Joeseph R. 的回答中所演示的那样),并在给定某些条件的情况下递归回它。
-e :n -e
- 可移植脚本将使用ewline 或新的内联执行语句来sed
分隔标签定义。
:
\n
-e
:n
- 定义一个名为 的标签n
。可以随时使用 或 返回此bn
内容tn
。tn
- t
est 命令返回到指定的标签(或者,如果没有提供,则退出当前行周期的脚本),如果s///
自定义标签或自上次调用t
ests 以来有任何替换成功。在此命令中,对匹配行进行递归。如果成功用其他字符sed
替换模式,则返回标签并重试。如果未执行替换,则会自动打印模式空间并开始下一个行周期。sed
:n
s///
sed
这往往可以更好地处理连续序列。如果最后一个失败,则打印:
first other characters other characters other characters line and so on
Run Code Online (Sandbox Code Playgroud)
如前所述,这里的逻辑与上一个非常相似,但测试更加明确。
/"$/bn
- 这是sed
测试。因为b
ranch 命令是该地址的函数,所以只有在附加 ewline 且模式空间仍以双引号结束后sed
才会b
ranch 返回。:n
\n
"
N
在和 之间尽可能少地完成b
- 通过这种方式sed
可以非常快速地收集所需的精确输入,以确保下一行无法匹配您的规则。替换s///
的不同之处在于它使用g
全局标志 - 因此它将立即进行所有必要的替换。给定相同的输入,此命令的输出与上一个命令相同。
归档时间: |
|
查看次数: |
12288 次 |
最近记录: |