我正在学习sed。一切似乎都很顺利,直到我遇到 N(接下来是多行)。我创建了这个文件 (guide.txt) 用于练习/理解/上下文目的。这是所述文件的内容...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Run Code Online (Sandbox Code Playgroud)
所以我的目标是用“系统用户”替换“网络管理员”的所有实例。因为“网络管理员”的第一个实例由换行符 (\n) 分隔,我需要多行下一个运算符 (N) 来附加以“管理员”开头的行,前一行以“网络\n”结尾. 没问题。但我也想捕获所有其他“网络管理员”单行实例。
从我的研究中,我了解到我需要两个替换命令;一个用于换行符分隔的字符串,另一个用于其他字符串。此外,由于最后一行包含替换匹配和多行,因此发生了一些 jive。所以我制作这个...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Run Code Online (Sandbox Code Playgroud)
这将返回这些结果...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Run Code Online (Sandbox Code Playgroud)
我认为单行替换会捕获“网络管理员”的所有“正常”实例并将其替换为“系统用户”,而多行语句会在换行符分隔的实例上发挥其魔力,但是当你可以看到它返回,我认为,意外的结果。
经过一番摆弄后,我降落在这个......
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
Run Code Online (Sandbox Code Playgroud)
瞧,我得到了所需的输出......
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Run Code Online (Sandbox Code Playgroud)
为什么这行得通而原始 sed 脚本不行?我真的很想了解这一点。
在此先感谢您的帮助。
首先,请注意您的解决方案并不真正有效。考虑这个测试文件:
$ cat test1
Network
Administrator Network
Administrator
Run Code Online (Sandbox Code Playgroud)
然后运行命令:
$ sed '
s/Network Administrator/System User/
N
s/Network\nAdministrator/System\nUser/
s/Network Administrator/System User/
' test1
System
User Network
Administrator
Run Code Online (Sandbox Code Playgroud)
问题是代码不能替代最后一个Network\nAdministrator
.
此解决方案确实有效:
$ sed ':a; /Network$/{$!{N;ba}}; s/Network\nAdministrator/System\nUser/g; s/Network Administrator/System User/g' test1
System
User System
User
Run Code Online (Sandbox Code Playgroud)
我们也可以将其应用于您的guide.txt
:
$ sed ':a; /Network$/{$!{N;ba}}; s/Network\nAdministrator/System\nUser/g; s/Network Administrator/System User/g' guide.txt
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Run Code Online (Sandbox Code Playgroud)
关键是继续按行阅读,直到找到不以Network
. 完成后,可以进行替换。
兼容性说明:以上均\n
在替换文本中使用。这需要GNU sed。它不适用于 BSD/OSX sed。
[给菲利普斯的帽子提示。]
如果它有助于澄清,这里是分割成多行的相同命令:
$ sed ':a
/Network$/{
$!{
N
ba
}
}
s/Network\nAdministrator/System\nUser/g
s/Network Administrator/System User/g
' filename
Run Code Online (Sandbox Code Playgroud)
:a
这将创建一个标签a
。
/Network$/{ $!{N;ba} }
如果这一行以 结尾Network
,那么,如果这不是最后一行 ( $!
),则读取并追加下一行 ( N
) 并分支回到标签a
( ba
)。
s/Network\nAdministrator/System\nUser/g
使用中间换行符进行替换。
s/Network Administrator/System User/g
用中间空白代替。
使用 GNU sed(不是BSD/OSX),我们只需要一个替代命令:
$ sed -zE 's/Network([[:space:]]+)Administrator/System\1User/g' test1
System
User System
User
Run Code Online (Sandbox Code Playgroud)
在guide.txt
文件上:
$ sed -zE 's/Network([[:space:]]+)Administrator/System\1User/g' guide.txt
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Run Code Online (Sandbox Code Playgroud)
在这种情况下,-z
告诉 sed 最多读入第一个 NUL 字符。由于文本文件永远不会有空字符,这具有一次读取整个文件的效果。然后我们可以进行替换而不必担心会丢失一行。
如果文件很大(通常意味着千兆字节),则此方法不好。如果它那么大,那么一次全部读入可能会使系统 RAM 紧张。
正如Phillipos所建议的,以下是一个便携式解决方案:
sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1Us??er/g'
Run Code Online (Sandbox Code Playgroud)
在您学习的过程中sed
,我会花时间补充@John1024 的回答:
1) 请注意,您使用\n
的是替换字符串。这适用于 GNU sed
,但不是 POSIX 的一部分,因此它会n
在许多其他sed
s 中插入反斜杠和 an (\n
在模式中使用是可移植的,顺便说一句)。
相反,我建议这样做s/Network\([[:space:]]\)Administrator/System\1Us??er/g
:[[:space:]]
将匹配换行符或空格,因此您不需要两个s
命令,而是将它们合二为一。通过将它包围起来,\(...\)
您可以在替换中引用它: The\1
将被第一对中匹配的任何内容替换\(\)
。
2)要正确匹配两行模式,您应该知道N;P;D
模式:
sed '$!N;s/Network\([[:space:]]\)Administrator/System\1User/g;P;D'
Run Code Online (Sandbox Code Playgroud)
将N
始终追加下一行(除了最后一行,这就是为什么它与“解决” $!
(=如果不是最后一行,你应该总是考虑到preceedN
以$!
避免意外结束脚本)然后更换后,P
只打印模式空间中的第一行,然后D
删除该行并使用模式空间的剩余部分开始下一个循环(不读取下一行)。这可能是您最初的意图。
记住这个模式,你会经常需要它。
3)多行编辑的另一种有用模式,尤其是当涉及两行以上时:保持空间收集,正如我向约翰建议的那样:
sed 'H;1h;$!d;g;s/Network\([[:space:]]\)Administrator/System\1Us??er/g'
Run Code Online (Sandbox Code Playgroud)
我重复它来解释它:H
将每一行附加到保持空间。由于这会导致在第一行之前有一个额外的换行符,因此需要移动第一行而不是附加1h
. 下面的$!d
意思是“对于除最后一行之外的所有行,删除模式空间并重新开始”。因此,脚本的其余部分仅在最后一行执行。此时,整个文件都被收集在保留空间中(所以不要将它用于非常大的文件!)并将其g
移动到模式空间,因此您可以像使用以下-z
选项一样立即进行所有替换GNU sed
。
这是我建议记住的另一个有用的模式。