$ (echo hello; echo there) | sed ':a;$!N;s/\n/string/;ta'
hellostringthere
上面的sed
命令用字符串“ string”替换换行符。但是我不知道:a;$!N;s/\n/string/;ta
单引号内的含义。我知道中间部分s/\n/string/
。但是我不知道first(:a;$!N;
)和last(ta
)部分的功能。
$ (echo hello; echo there) | sed ':a;$!N;s/\n/string/;ta'
hellostringthere
上面的sed
命令用字符串“ string”替换换行符。但是我不知道:a;$!N;s/\n/string/;ta
单引号内的含义。我知道中间部分s/\n/string/
。但是我不知道first(:a;$!N;
)和last(ta
)部分的功能。
Answers:
这些是公认的神秘sed
命令。具体而言(来自man sed
):
:label
b和t命令的标签。t label
如果as ///自从最后一条输入行被读取以来以及从最后的t或T命令以来已成功完成替换,则跳转到label;如果省略了label,则跳转到脚本结尾。n N将下一行输入读/追加到模式空间。
因此,您发布的脚本可以分解为(为便于阅读而添加的空格):
sed ':a; $!N; s/\n/string/; ta'
--- ---- ------------- --
| | | |--> go back (`t`) to `a`
| | |-------------> substitute newlines with `string`
| |----------------------> If this is not the last line (`$!`), append the
| next line to the pattern space.
|----------------------------> Create the label `a`.
基本上,这可以用伪代码编写为
while (not end of line){
append current line to this one and replace \n with 'string'
}
通过更复杂的输入示例,您可以更好地理解这一点:
$ printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;$!N;s/\n/string/;ta'
line1stringline2stringline3stringline4stringline5
我不太确定为什么!$
需要它。据我所知,您可以获得与
printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;N;s/\n/string/;ta'
$!
不是!$
。但是,也可能!N
不是$!
。
!N
或的引用$!
。因此,我仍然保持我的想法,那就是在寻找最后一行是换行符还是EOF。
$!
视为带有后缀补码运算符的地址“范围”-因此$!N
(N
除address之外的所有操作$
)实际上与诸如m,n!d
(删除m
to 以外的所有内容n
)的语法相同。
我发布这个答案,因为我看到一个关于很多困惑,为什么在执行时的最后一行被排除N
(通过行写入字符串$!
),并因为OP感到困惑的含义:a;$!N;
在一个 sed命令,不仅在特定的一个,他发布。
好吧,在提议的示例中(由OP和@terdon使用),使用$!N
代替代替的好处N
并不明显,因为在N
命令后的最后一行没有执行“重要”(保持读取)命令。(实际上,如果剥离该行地址,结果是相同的。)
在一个更复杂的示例中(例如,替换this sentence
为一个文件,两个单词有时出现在一行中,而另一些时候则出现在两行中),排除N
命令的最后一行可能至关重要!如果最后一行不排除,一旦执行N
就可以了,sed
撞击EOF
和出口立即,防止所有的后续命令(分支指令为好,即t
与b
将被执行)。
在所示的过于简单的示例中,我们可以安全地删除$!
并让sed
执行失败N
并返回失败,因为中止的s
命令如果执行则不会执行任何操作,因为没有\n
匹配项。