对bash中的文本文件中的段落进行排序

问题描述:

sort实用程序让我们可以方便地对文件中的行进行排序。但是,有没有一种优雅的方式来排序在bash中空行分隔的段落?对bash中的文本文件中的段落进行排序

例如

ccc 
aa 

aba 
bbb 

aba 
ccc 

aaa 

将不得不成为

aaa 

aba 
bbb 

aba 
ccc 

ccc 
aa 

一种解决方案似乎是所有非空行更换新线符号:

ccc\naa  
aba\nbbb 
aba\nccc 
aaa 

然后调用运行sort

aaa 
aba\nbbb 
aba\nccc 
ccc\naa  

,然后恢复新线:

aaa 

aba 
bbb 

aba 
ccc 

ccc 
aa  
+1

你能澄清一下为什么你可以使用'sort'工具,而不是'sed'工具吗?我真的没有看到这种区别。你还可以使用哪些其他工具,而不是用于? – ruakh

+0

另外 - 您的系统的“排序”支持使用\ 0而不是\ n作为分隔符吗? – ruakh

+0

@ruakh我将编辑该问题。如果你有一个'sed'解决方案,我确实很好。 – john1234

Perl来拯救;

perl -n00 -e 'push @a, $_; END { print sort @a }' file 

-00选项使“段落模式”其将上空行输入。

如果 - 与您的示例中一样 - 最后的输入行不一定为空,则需要单独添加一个换行符。

perl -n00 -e 'push @a, $_; 
    END { $a[-1] .= "\n" if $a[-1] !~ /\n\n$/; 
     print sort @a }' file 
+0

我非常喜欢这一点。但是你确定段落中的换行符(字段之间)不会影响排序吗? – hek2mgl

+0

想想看,这取决于用例。让我们看看OP说的话。 – hek2mgl

+0

这是一个简单的词汇排序。换行符在最后。如果最后一段在它后面没有分隔符,则需要稍微调整输出(使用OP的输入,我将'aaa'紧靠'aba bbb',因此它看起来像是单个记录)。 – tripleee

可能是它并不完美,但它的工作对你的输入。

#!/bin/bash 

par="" 
while read line 
do 
    if [ "${#line}" -gt 0 ]; then 
read -d '' par <<EOF 
$par 
$line 
EOF 

    fi 
    if [ "${#line}" -eq 0 ]; then 
    sort <<< "$par" 
    par="" 
    echo  
    fi 
done < "${1:-/dev/stdin}" 

我会使用不可打印的字符作为分隔符。比方说\1


您可以使用awk翻译这个文件,然后对它进行排序,然后用awk把它翻译回:

awk '{$1=$1}1' RS='' OFS='\1' file \ 
    | sort -i \ 
    | awk '{$1=$1}1' FS='\1' OFS='\n' ORS='\n\n' 

$1=$1是一个无操作操作,但它仍然告诉awk来重新组装使用OFS和/或ORS分隔符记录。所有的逻辑表示使用分隔符:

首先AWK命令

  • RS=''是记录分离器的特殊值。如果RS是空字符串,则默认为两个或更多后续新行,这些行可以有效地按段落分割。在这种情况下,字段由新行分隔。
  • OFS='\1'在输出中分隔字段\1。输出记录分隔符默认为一个换行符。

这给我们:

ccc<garbage>aa 
aba<garbage>bbb 
aba<garbage>ccc 
aaa 

我们现在可以sort -i说。-i忽略非打印字符,这给我们:

aaa 
aba<garbage>bbb 
aba<garbage>ccc 
ccc<garbage>aa 

第二awk命令

  • FS='\1'分割输入由\1
  • OFS='\n'字段设置输出字段分隔符为换行符
  • ORS='\n\n'将输出记录分隔符设置为两个换行符,这实际上是一个空格y线。

输出:

aaa 

aba 
bbb 

aba 
ccc 

ccc 
aa 

注意,该解决方案将不会保留超过段落之间的单个新行了。