需要一些 sed 魔法:将标记行移动到文件的开头

需要一些 sed 魔法:将标记行移动到文件的开头

我发现自己急需一些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通过覆盖模式空间来保留空间,然后第一个\newline 字符被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    

相关内容