Answers:
使用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解决方案)在输入文件的末尾需要一个空白行,以便按预期工作。
可能有一种方法可以执行此操作sed
,但是我怀疑它会很简单。这是我在Perl中的做法:
perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file
之所以可行,是因为将输入记录分隔符定义为空字符(-00
)会告诉Perl以段模式进行操作。Perl对段落1的定义与您的定义完全匹配。
1在标题下查看Other values for $/
如果您的段落始终以单行分隔:
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
在输入)。
您可以使用的单个实例来完成此操作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/
。如果没有此操作,它将不会是一个字面的一线工具,因为它将包含一个反斜杠换行符。)
G;h
。您可能会提到有关输入限制或类似内容的内容。
sed
便利,但是脚本版本确实保留了段落之间的空白。我只是在您的输入上对其进行了测试。您是否测试了脚本版本?