文件中段落的相反顺序


8

我有一个在段落中包含文本的文件(文本行由一个或多个空行分隔)。我想反转段落的顺序(即最后一段将成为第一段,...),最好使用sed。

我正在寻找一个sed命令,该命令对段落tac文件有效,对行文件有效。

Answers:


6

使用sed并不像约瑟夫·R所提到的那样直接。但是,您可以说:

sed '/./{H;d;};x;s/\n/={NL}=/g' inputfile | \
sed -e 's/^={NL}=//' -e '1!G;h;$!d' | \
sed G | sed 's/={NL}=/\'$'\n/g'

给定样本输入:

Para 1 line 1
Para 1 line 2
Para 1 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 3 line 1
Para 3 line 2
Para 3 line 3

这将产生:

Para 3 line 1
Para 3 line 2
Para 3 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 1 line 1
Para 1 line 2
Para 1 line 3

值得一提的是,此解决方案(以及替代的Perl解决方案)在输入文件的末尾需要一个空白行,以便按预期工作。


6

此解决方案同时使用tacperl来读取段落。它不需要将整个文件读入内存。

tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'

反转文件的所有行,然后对于每个反转的段落,反转行。


这看起来非常优雅和高效。但是,此解决方案也将多条空(即分离)线浓缩为一条
Martin Vegter 2014年

3

可能有一种方法可以执行此操作sed,但是我怀疑它会很简单。这是我在Perl中的做法:

perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file

之所以可行,是因为将输入记录分隔符定义为空字符(-00)会告诉Perl以段模式进行操作。Perl对段落1的定义与您的定义完全匹配。


1在标题下查看Other values for $/


这确实有效。唯一的小问题是,它不会保留分隔段落的多个空行。取而代之的是,所有段落均由一个空行分隔。
Martin Vegter 2014年

1

如果您的段落始终以单行分隔:

sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed 's/^\x03//;1s/\x03$//;1!G;h;$!d;$a\' | tr $'\003' \\n

这是很容易看到,如果你打破它成片,并运行它是如何工作 sed '/^$/s/^/\x02/' infile,然后sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n等...


如果您的段落用一个或多个空行分隔,例如

Para 1 line 1
Para 1 line 2

Para 2 line 1


Para 3 line 1
Para 3 line 2

Para 4 line 1
Para 4 line 2



Para 5 line 1

而您想颠倒段落的顺序但保留“空块”的顺序,则可以读取文件两次:
第一个:将段落变成单行(删除中间的空块)并颠倒它们;
第二个:转动空的块分成几行,“索引”每个块中的空行数(并删除非空行),
然后返回paste结果并处理输出以恢复换行:

paste -d $'\004' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) \
| sed '$!s/\x04/\n/;$s/\x04$//' | tr $'\003\002' \\n\\n

输出:

Para 5 line 1

Para 4 line 1
Para 4 line 2


Para 3 line 1
Para 3 line 2

Para 2 line 1



Para 1 line 1
Para 1 line 2

如果您不介意输出中有多余的尾行,则可以删除最后一个sed

paste -d $'\n' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) | \
tr $'\003\002' \\n\\n

这些假设的第一和最后一行不为空(没有\x02\x03\x04在输入)。


1

您可以使用的单个实例来完成此操作sed;无需管道。由于sed仅使文档一次通过,并且由于输出开始所需的文件部分位于文件的末尾,因此它将需要将整个文件保存在内部的内存中sed(在保存空间中),因此可能伸缩性不好。但这恰好回答了这个问题:

:getpara
   ${
      s/$/\
/
      G
      s/\n\n$//
      q
   }
   N
   /\n$/!bgetpara
G
h
$!d
s/\n\n$//
q

如果没有尾随换行符,它仍然可以正常工作。如果只有一条尾随的换行符,则会在输出中将其抑制(即,在输出中将不会有前导的换行符)。例如,如果输入中有5条尾随换行符,则输出中将有4条前导换行符。

段落之间的差距得以保留。

空白行上的空格不会被视为段落中断,但这是一个功能,而不是错误。:)

您也可以这样做,因为它的可读性差得多:

sed ':k;${;s/\(\(\n\).*\)$/\1\2/;G;s/\n\n$//;q;};N;/\n$/!bk;G;h;$!d;s/\n\n$//;q' inputfile

虽然这仅适用于GNU sed。(请注意,要使用反向引用来执行技巧s/$/\n/。如果没有此操作,它将不会是一个字面的一线工具,因为它将包含一个反斜杠换行符。)


所以你把文件拖出来吧?好像您把整个东西都放在了容纳空间中。w / G;h。您可能会提到有关输入限制或类似内容的内容。
mikeserv

我没有测试单行代码,因为我正在Mac上工作,并且没有使用GNU的sed便利,但是脚本版本确实保留了段落之间的空白。我只是在您的输入上对其进行了测试。您是否测试了脚本版本?
2015年

@mikeserv:绝对正确。(今晚将进行更新。)
通配符2015年

0
gem install facets

ruby -r facets/string \
     -e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file

这应该保留您的段落间距(虽然比sed:更具可读性))尽管可以通过devnull获得一个很棒的答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.