使用sed获取包含正则表达式的所有行并附加到文件末尾

使用sed获取包含正则表达式的所有行并附加到文件末尾,sed,Sed,我正试图想出一个sed脚本来获取包含模式的所有行,并将它们移动到输出的末尾。这是一个学习hold vs pattern space的练习,我正努力想出来(尽管我感觉很接近) 我在这里: $ echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" | sed -E '/foo/H; //d; $G' hi bar something yo foo1 foo2 但我希望输出是: hi bar something yo foo1 foo2 我明白为什么会这样。这

我正试图想出一个
sed
脚本来获取包含模式的所有行,并将它们移动到输出的末尾。这是一个学习hold vs pattern space的练习,我正努力想出来(尽管我感觉很接近)

我在这里:

$ echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" | sed -E '/foo/H; //d; $G'
hi
bar
something
yo

foo1
foo2
但我希望输出是:

hi
bar
something
yo
foo1
foo2
我明白为什么会这样。这是因为我们第一次发现<代码> FoO 保持空间是空的,所以H将代码> > \n>代码>到空白保持空间,然后是第一个FO,我想这很好。但是,$G又做了一次,即另一个追加,它将
\n
加上保持空间中的内容附加到模式空间

我尝试了一个最终的删除命令,使用了<代码> /^ $/d < /代码>,但是它没有删除空白行(我想这是因为这个模式不是与最后一行匹配的,而是针对现在有多行模式空间的,它里面有<代码> \n \n>代码> 我相信

sed
大师会为我解决问题。

这可能对你有用(GNU-sed):

如果该行包含所需字符串,则将其附加到保留空间(HS)中,否则按正常方式打印。如果该行不是最后一行,则将其删除,否则将HS替换为图案空间(PS)。如果所需字符串现在在PS中(HS是什么);由于所有这些图案都是附加的,因此第一个字符将是换行符,请删除第一个字符并打印。删除剩余的内容

另一种方法是使用
-n
标志:

sed -n '/foo/H;//!p;$!b;x;//s/.//p' file

注意:当
d
B
时(无参数)命令被执行,不再执行sed命令,一行新行被读入PS,sed脚本从第一个命令开始,即sed命令不会在前一个
d
命令之后恢复。

这感觉像是一次黑客攻击,我认为应该可以更优雅地处理这种情况。以下内容适用于GNU
sed

echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" | sed -r '/foo/{H;d;}; $G; s/\n\n/\n/g'
但是,在OSX/BSD
sed
上,会产生这种奇数输出:

hi
bar
something
yonfoo1
foo2
注意:两个连续的换行符已替换为文字字符
n

本文解释了OSX/BSD与GNU sed之间的关系,以及以下工作(在GNU sed中也有):


在BSD sed中,它不接受替换表达式的RHS中的转义字符,因此您必须在命令行中放入一个真正的LF/换行符,或者执行上面的操作,在RHS中需要换行符的地方拆分sed脚本字符串,并在“\n”前面加一个美元符号,这样shell就可以了。

为什么在awk中,is是绝对微不足道的,awk在sed的任何地方都可以使用,因此产生的awk脚本将比sed脚本更简单、更可移植、更快、更好地完成同样的任务。在1970年代中期awk发明之前,sed中的所有存储空间都是必要的,但现在它完全没有用处了除了作为一种心理锻炼

$ echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" |
    awk '/foo/{buf = buf $0 RS;next} {print} END{printf "%s",buf}'
hi
bar
something
yo
foo1
foo2

以上内容将在每个UNIX安装上的每个awk中都能正常工作,我打赌您可以很容易地找到它的工作原理。

为什么不发布您的解决方案作为答案呢?这里实际上鼓励您这样做。谢谢。//s///p是做什么的?我认为s///p表示匹配单个字符(本例中为换行符)但是前面的//是什么呢?嗯,你能用//is-effective/foo/s///p中的“last regex-matched”技巧来替换s吗?如果PS与foo匹配,那么进行替换吗?
echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" | sed '/foo/{H;d;}; $G; s/\n\n/\'$'\n''/' 
$ echo -e "hi\nfoo1\nbar\nsomething\nfoo2\nyo" |
    awk '/foo/{buf = buf $0 RS;next} {print} END{printf "%s",buf}'
hi
bar
something
yo
foo1
foo2