【问题标题】:sort multiple line record with awk使用 awk 对多行记录进行排序
【发布时间】:2018-06-26 21:14:41
【问题描述】:

我有一个文件,其中的记录如下所示:

nad9
   abie_by_ctai_prots   contig_4729                         808,  1393     1,196   abie_by_ctai_prots_1_196
   abie_by_wmir_prots   contig_4729                         811,  1363     2,187   abie_by_wmir_prots_2_187
   abie_by_gbil_prots   contig_4729                         808,  1393     1,196   abie_by_gbil_prots_1_196
   abie_by_atha_prots   contig_4729                         808,  1363     1,186   abie_by_atha_prots_1_186

ND2
   abie_by_ctai_prots   contig_1280                        9618, 11661     0,182   abie_by_ctai_prots_0_182
   abie_by_ctai_prots   contig_9528                         770,   959   427,490   abie_by_ctai_prots_427_490
   abie_by_ctai_prots   contig_6628                        5874,  2217   182,429   abie_by_ctai_prots_182_429

ccmB
   abie_by_ctai_prots   contig_334                        39851, 39218     0,212   abie_by_ctai_prots_0_212
   abie_by_wmir_prots   contig_334                        39842, 39218     2,211   abie_by_wmir_prots_2_211
   abie_by_gbil_prots   contig_334                        39851, 39218     0,212  

我想根据基因名称(记录的第一行)对记录进行排序。 输出应如下所示:

ND2
   abie_by_ctai_prots   contig_1280                        9618, 11661     0,182   abie_by_ctai_prots_0_182
   abie_by_ctai_prots   contig_9528                         770,   959   427,490   abie_by_ctai_prots_427_490
   abie_by_ctai_prots   contig_6628                        5874,  2217   182,429   abie_by_ctai_prots_182_429

ccmB
   abie_by_ctai_prots   contig_334                        39851, 39218     0,212   abie_by_ctai_prots_0_212
   abie_by_wmir_prots   contig_334                        39842, 39218     2,211   abie_by_wmir_prots_2_211
   abie_by_gbil_prots   contig_334                        39851, 39218     0,212   abie_by_gbil_prots_0_212

nad9
   abie_by_ctai_prots   contig_4729                         808,  1393     1,196   abie_by_ctai_prots_1_196
   abie_by_wmir_prots   contig_4729                         811,  1363     2,187   abie_by_wmir_prots_2_187
   abie_by_gbil_prots   contig_4729                         808,  1393     1,196   abie_by_gbil_prots_1_196
   abie_by_atha_prots   contig_4729                         808,  1363     1,186   abie_by_atha_prots_1_186

我试过这段代码没有成功:
vilde$ awk '{ RS = ""; FS = "\n"} {print $0}' |sort filename.txt

它给我的输出看起来类似于:

(empty line)    
(empty line)
(empty line)  
abie_by_ctai_prots   contig_4729                         808,  1393     1,196   abie_by_ctai_prots_1_196
abie_by_wmir_prots   contig_4729                         811,  1363     2,187   abie_by_wmir_prots_2_187
abie_by_gbil_prots   contig_4729                         808,  1393     1,196   abie_by_gbil_prots_1_196
abie_by_atha_prots   contig_4729                         808,  1363     1,186   abie_by_atha_prots_1_186
ND2   
ccmB
nad9

在我看来,它是对字段而不是记录进行排序,但我不明白为什么或如何改变这一点。

【问题讨论】:

  • sort 对行进行排序。我不确定你想在这里用awk 做什么。
  • 我对此完全不感兴趣,所以我可能会以错误的方式处理这个问题。我是否可以临时替换所有新行以使每条记录都放在一行上。这样我可以使用排序吗?
  • 如果输入不是太大,我会做类似perl -00 -e 'print sort readline'的事情。

标签: awk text-processing


【解决方案1】:

有几种方法可以做到这一点:

一个小文件: 如果你想对一个小文件进行排序,你可以使用 GNU awk 并使用 PROCINFO["sorted_in"]="@ind_str_asc" ,它可以让你按索引升序遍历数组。

awk 'BEGIN{RS=""; ORS="\n\n"; FS="\n"
           PROCINFO["sorted_in"]="@ind_str_asc" }
     {a[$1]=$0}
     END{for(i in a) { print a[i] } }' <inputfile> > <outputfile>

一个巨大的文件:如果你想用一个非常大的文件来做这件事,那么 awk 会卡住它,所以你必须用一些 awk、@987654326 来做一些不同的事情@ 和 cat 的东西。这个想法是创建大量具有正确名称的文件,然后对文件进行排序并将它们分类:

#!/usr/bin/env bash
inputfile=$1
outputfile=$2

dir=$(mktemp -d)
awk -v dir=$dir 'BEGIN{RS=""; ORS="\n\n"; FS="[[:blank:]]*\n"}
     { fname=dir"/"$1; print $0 > fname; close(fname) }' $inputfile
export LC_ALL=C
files=( $dir/* )
sort <<< ${files[*]} | xargs cat > $outputfil
rm -rf $dir

或者你可以只使用一个大管道:

awk 'BEGIN{RS="";FS="\n";OFS="|"}{gsub(FS,OFS)}1' <inputfile> | sort \
   | awk 'BEGIN{ORS="\n\n";OFS="\n";FS="\\|"}{gsub(FS,OFS)}1' > <outputfile>

注意:我假设您的文件中没有 Windows \r\n。您的原始输入表明情况确实如此。

有用的链接:

【讨论】:

  • 谢谢!我尝试了两者,它与一个小文件完美配合。但是对于我的完整文件,我收到错误: cat:rps19:没有这样的文件或目录 cat:rps2:没有这样的文件或目录 cat:rps3:没有这样的文件或目录 cat:rps4:没有这样的文件或目录
  • 谢谢!单个管道适用于大文件。
【解决方案2】:

您问题中的命令行似乎没有为awk 命令提供输入,因此您只是对输入文件的各个行进行排序。但是您与RS="" 走在了正确的轨道上。

据我所知,大多数排序实现不会处理单个记录的多行输入。但是您的记录看起来像awk 可以很好地处理的那种东西,所以我认为我的方法是使用管道来转换记录中的换行符以允许对记录进行排序,然后在排序后将它们转换回来。像这样:

$ awk -v RS= '{gsub(/\n/,"#")} 1' input.txt | sort | awk '{gsub(/#/,"\n")} 1'

请注意,这不会在记录之间放置空行。如果您需要这些,请将最后的 1 替换为:{print $0 ORS}

【讨论】:

    【解决方案3】:

    如果您的输入是文本文件(例如,其中没有空字节),您可以进行一些预处理/后处理。我的 perl 有点生疏,但这里有一个简单的方法,用空字节替换记录中的每个换行符,然后使用排序,然后放回换行符。

    perl -e 'while(<>){ chop; $p .=  ($_ eq "") ? "\n" : "\000" ; 
        print $p; $p=$_; }' input.txt | sort | perl -pe 's/\000/\n/g'
    

    写成这样可能更简洁一些:

    < input.txt perl -000 -lape 's/\n/\000/g' | 
        sed '/^$/d' | sort | 
        perl -ne 's/\000/\n/g; print $_ . "\n"'
    

    使用段落 slurping(而不是整个文件)是通过不将所有内容都放入内存来启用大文件的毫无意义的尝试。 (毫无意义,因为如果问题是数据的大小足以引起问题,那么sort 会窒息。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-11
      • 2019-08-04
      • 2014-09-26
      • 2011-04-21
      • 2014-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多