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

6
sort工具可以方便地对文件中的行进行排序。然而,在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
还有,你们的系统“sort”支持使用\0作为分隔符而不是\n吗? - ruakh
@ruakh 我会编辑问题。如果你有 sed 的解决方案,我实际上很满意。 - john1234
@ruakh 是的,我的 sort 支持 --zero-terminated 参数。 - john1234
4个回答

8
< p > Perl来解救;< /p >
perl -n00 -e 'push @a, $_; END { print sort @a }' file
< p > -00 选项可以启用“段落模式”,它会在空行处分割输入。

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

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

我一开始非常喜欢这个。但是你确定段落中的换行符(在字段之间)不会破坏排序吗? - hek2mgl
想一想,这取决于使用情况。让我们看看原帖作者说了什么。 - hek2mgl
这是一个简单的词法排序。换行符在末尾。如果最后一段缺少分隔符,您需要稍微调整输出(使用OP的输入,我得到aaa紧邻aba bbb,所以看起来它们是一个记录)。 - tripleee
可能我想得太复杂了。我喜欢这个解决方案,让我们看看它是否有效 :) (看起来,你是对的) - hek2mgl

2
在空行上放置空字节(并在开头再放一个),使用sort -z,然后删除空字节。最终你会在开头多出一个额外的换行符,你可以使用tail来去掉它。
使用echo + sed:
(echo '\0'; cat myfile) |
    sed 's/^$/\x0/' |
    sort -z |
    tr -d '\000' |
    tail -n+2

或者使用 awk:

awk 'BEGIN{print "\0"}
    /^$/{printf "\0"} {print $0}' myfile |
sort -z |
tr -d '\000' |
tail -n+2

0

也许它不完美,但它适用于您的输入。

#!/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}"

也许应该指出,在Bash中,“while read”非常低效。此外,你真的想在这里使用“read -r”。 - tripleee

0

我会使用不可打印字符作为分隔符号。比如说\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' 将输出记录分隔符设置为两个换行符,这实际上是一个空行。

输出:

aaa

aba
bbb

aba
ccc

ccc
aa

请注意,此解决方案不会保留段落之间超过一个换行符的空白行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接