我无法理解这个 sed 脚本是如何工作的:
echo -e "Line #1\n\n\n\nLine #2\n\n\nLine #3" | sed '1s/^$//p;/./,/^$/!d'
它会抑制重复的空行,例如cat -s
“但是我有几个问题”:
- 为了什么
1s/^$//p
?据我了解,即使第一行为空,它也不执行任何操作 - 这是否只在第一个喜欢
/./,/^$/
之前匹配而不匹配?^$
Line #1\n\n
Line #1\n\n\n
- sed 中默认范围不是贪婪的吗?
为了澄清问题 3,我尝试了接下来的测试:
echo -e "Line #1\n\n\n\nLine #2\n\n\nLine #3" | sed -n '/#/,/#/p'
结果是:
Line #1
Line #2
Line #3
(所以,它是贪婪的)
但是当我尝试时:
echo -e "Line #1\n\n\n\nLine #2\n\n\nLine #3" | sed -n '/#1/,/#/p'
结果是:
Line #1
Line #2
(现在看来是不贪心了)
答案1
1s/^$//p
如果第一行为空,则打印第一行。
/./,/^$/
匹配从第一个非空行到遇到的第一个空行的行。从正则表达式限定符的意义上来说,它不是贪婪的:sed
无法向前查看文件或回溯,因此它必须在结束模式第一次匹配时停止。
在结束行之后,再次开始搜索起始行,因此下一个非空行再次开始范围。实际上,范围匹配连续的非空行,加上后面的第一个空行。
由于范围被用作/./,/^$/!d
,因此所有不匹配的行都将被删除。这包括第一行(如果它是空的),这就是为什么第一条规则显式打印它的原因。
如果没有1s/^$//p
规则,第一行如果为空就会被删除,即使它并不是真正的“重复”。
$ echo $'\nfoo' | sed '1s/^$//p;/./,/^$/!d'
foo
$ echo $'\nfoo' | sed '/./,/^$/!d'
foo
$
在您的测试中,范围/#/,/#/
有点不同,因为它以相同的模式开始和结束。Line #1
匹配开始模式,(因此打印中间的空行)Line #2
匹配结束模式,(后面的空行不是)并且 on Line #3
,范围再次开始。
在另一个中,起始模式是/#1/
,但仅在输入中找到一次。
答案2
ilkkachu 给出了一个很好的答案,涵盖了该命令的各个方面,包括对 sed 字符串第一部分的不太明显的需求;并包括“!”这一事实字符删除不匹配的部分,实际上是重复的空行。
尽管如此,当有更简单的选择时,这仍然是一种奇怪的方法,例如更多-s, 或者独特的可用的。