![需要一些 sed 魔法:将标记行移动到文件的开头](https://linux22.com/image/33406/%E9%9C%80%E8%A6%81%E4%B8%80%E4%BA%9B%20sed%20%E9%AD%94%E6%B3%95%EF%BC%9A%E5%B0%86%E6%A0%87%E8%AE%B0%E8%A1%8C%E7%A7%BB%E5%8A%A8%E5%88%B0%E6%96%87%E4%BB%B6%E7%9A%84%E5%BC%80%E5%A4%B4.png)
我发现自己急需一些sed
魔法(我真的我需要坐下来学习这一点)。我有一个包含很多行的文件。审阅之后,我通过在行首添加星号 (*) 来标记一些行。
我想要做的一些sed
技巧(如果可能的话)是将所有标记行移动到文件的开头(或结尾 - 我不挑剔),这样它们就形成一个块。其他线路(未标记)不应受到干扰。
我怎样才能做这样的事情sed
?我知道sed
有一些用于移动文本的缓冲区......
答案1
sed 是必需的吗?如果您不介意两次遍历源文件,则可以使用 grep 轻松完成此操作。
例如
grep '^\*' input > outputfile
grep -v '^\*' input >> outputfile
答案2
根据文件的大小,您可以执行以下操作:
sed '/^*/!H;//p;$!d;g;s/\n//'
它堆叠在H
不匹配的旧空间线中/^*/
。那些匹配的内容会p
在输入中出现时被打印出来。然后,所有不是!
最后的行都将从输出中删除$
。d
在最后一行,我们g
通过覆盖模式空间来保留空间,然后第一个\n
ewline 字符被s/\n//
替换掉,因为第一H
行每次都会产生一个额外的字符。
但这需要一个大的缓冲区,因为它已将所有这些行存储在H
旧空间中。另一方面,这...
sed '/^*/p;$!d;g;r file' <file |
sed -e '1,/^$/{/./p;d' -e '};/^*/d'
……没有这个要求。
第一个sed
p
仅打印/^*/
匹配的行,直到$
最后一行,此时它打印一个空行,然后r
再次读出整个输入文件。
第二个sed
首先在从第一行到第一个空白行的行范围内工作,p
打印至少匹配单个字符的所有行,然后d
删除该批次。遇到第一个空行后,它会d
删除所有匹配的行/^*/
。
答案3
您不需sed
要这样做,您可以使用一些基本的 grep 将星号 (*) 行拉到顶部。比如说你有这个文件:
$ cat sample.txt
1
2
3
4
* 5
* 6
* 7
8
9
10
现在,grep
将星号 (*) 行放在前面的 example.txt 文件中:
$ cat <(grep '*' sample.txt) <(grep -v '*' sample.txt)
* 5
* 6
* 7
1
2
3
4
8
9
10
上面将运行 2 个 grep,第一个将所有带星号的行拉出,而第二个则拉出所有非星号的行。这两个命令的输出cat
使用<()
符号重定向为命令的输入。
替代方法
如果您不想使用 cat + 2 个子 shell,您可以按照@terdon 的建议进行操作:
$ grep '*' sample.txt; grep -v '*' sample.txt
这将拉出所有sample.txt
包含星号 (*) 的行,后跟所有不包含星号 (*) 的行。
参考
答案4
如果您不介意两个块之间的空行:
sed -n -e '/^* /{H;$!d}' -e '/^* /!p' -e '${g;p}'
或者反过来
sed -n -e '/^* /{p;$!d}' -e '/^* /!H' -e '${g;p}' file