Mar*_*cus 5 sed text-processing regular-expression
我了解到,使用 sed,可以通过使用空数据模式 ( -z
):来匹配模式的第 n 次出现sed -z 's/foo/bar/2'
。
使用时如何匹配行首-z
?
如果我执行:
echo $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/2'
Run Code Online (Sandbox Code Playgroud)
没有替换,因为^
表示整个字符串的开头:
$ echo $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/g'
baz
foo
foo
Run Code Online (Sandbox Code Playgroud)
在 Perl 中,有m
正则表达式修饰符(当 slurping 时),但在 sed 中它没有帮助。
echo $'foo\nfoo\nfoo' | sed -Ez 's/(^|\n)foo/\1baz/2'
Run Code Online (Sandbox Code Playgroud)
^
需要正确计算foo
恰好位于第一行开头的内容。
请注意,代码计算的是(^|\n)foo
,而不是 的出现次数foo
。如果您想对foo
s 进行计数,但仅当所需的出现恰好位于行的开头时才进行替换,那么此代码不是解决方案。例如在:
echo $'foo foo\nfoo foo\nfoo foo' | sed -Ez 's/(^|\n)foo/\1baz/3'
Run Code Online (Sandbox Code Playgroud)
被foo
替换的已经不是第三次了foo
。
使用 GNU sed
4.8 进行测试。
并不像所选答案看起来那么简单。2
首先, (或 s///2 中的任何其他数字)的实际含义是什么?它只是意味着应用它的行上的第二个正则表达式匹配将被更改。
在
$ printf '%s\n' 'foo foo foo' 'foo foo foo' 'foo foo foo' | sed 's/foo/bar/2'
foo bar foo
foo bar foo
foo bar foo
Run Code Online (Sandbox Code Playgroud)
sed 命令更改了foo
with bar
but应用于它的每一行(所有行)的第二个 (2) 实例。可以将其更改为仅在一行或一系列行上工作:
printf '%printf '%s\n' 'foo foo foo'{,,,,} | sed '3,4s/foo/bar/2'
foo foo foo
foo foo foo
foo bar foo
foo bar foo
foo foo foo
Run Code Online (Sandbox Code Playgroud)
请注意,只有行3
和4
被更改,而不是全部,并且在所有这些行中,foo
被更改的实例是第二个 (2)。
这就是工作原理s/foo/bar/2
。
现在,如果使用 if ,则行以(not )-z
结尾。但替换的工作方式完全相同(用代替):\0
\n
\0
\n
$ printf '%s\0' 'foo foo foo'{,,,,} | sed -z '3,4s/foo/bar/2' | xxd
00000000: 666f 6f20 666f 6f20 666f 6f00 666f 6f20 foo foo foo.foo
00000010: 666f 6f20 666f 6f00 666f 6f20 6261 7220 foo foo.foo bar
00000020: 666f 6f00 666f 6f20 6261 7220 666f 6f00 foo.foo bar foo.
00000030: 666f 6f20 666f 6f20 666f 6f00 foo foo foo.
Run Code Online (Sandbox Code Playgroud)
每行中没有echo $'foo\nfoo\nfoo' | sed -z 's/^foo/baz/2'
足够的内容可以更改第二行,但在下一个示例中:应该是吗?foo
$ printf 'foo foo foo\nfoo foo foo\n' | sed -z 's/^foo/baz/2'
foo foo foo
foo foo foo
Run Code Online (Sandbox Code Playgroud)
foo
哎呀,不,行首也没有足够的。问题是:一条线从哪里开始?在换行符处或在 a 处\0
,或两者?或者没有?
使用“-z”时考虑“行的开头”是没有意义的^
。
这是 sed 的内部混乱。请记住:使用-z
是实验性的,可能会导致奇怪的问题。
事实上,为了使替换正确工作,整个输入需要位于模式空间中。不,如果输入有 NUL ( ),则不起作用,\0
这些将被视为行分隔符(或者,用 awk 的说法,作为记录分隔符)。
$ printf 'foo\0foo\0foo\0' | sed -z 's/^foo/baz/2'
foofoofoo
Run Code Online (Sandbox Code Playgroud)
我们可以使用 sed 模式空间内的整个输入文件H;1h;$!d;x;.....
,然后尝试^foo
替换:
$ printf 'foo\0foo\0foo\0\n' | sed -z 'H;1h;$!d;x;l;s/^foo/ baz /M2'
foo\000foo\000foo\000\n$foo baz foo
Run Code Online (Sandbox Code Playgroud)
允许l
我们查看模式空间内的内容,并且M
需要该标志,以便 比^
第一行匹配更多。如果M
不使用 , 则^foo
仅在第一行(在模式空间的开头)匹配。
替代方案M
是:
$ printf 'foo\0foo\0foo\0' | sed -z 'H;1h;$!d;x;l;s/\(^\|\x0\)foo/ baz /2'
foo\000foo\000foo$foo baz foo
Run Code Online (Sandbox Code Playgroud)
请注意,尾随\0
被删除,进入内部模式空间的是foo\000foo\000foo
,它缺少尾随\0
,而尾随是在输入中明确提供的。
\0
我们可以通过添加尾随换行符来获取所有三个:
$ printf 'foo\0foo\0foo\0\n' | sed -z 'H;1h;$!d;x;l;s/\(^\|\x0\)foo/ baz /2'
foo\000foo\000foo\000\n$foo baz foo
Run Code Online (Sandbox Code Playgroud)
这清楚地表明 sed\0
有时将 a 视为分隔符,而\n
在其他情况下将 a 视为分隔符。
简而言之,带有该-z
选项的 sed 仍处于实验阶段。
归档时间: |
|
查看次数: |
668 次 |
最近记录: |