【问题标题】:How to concatenate lines based on one line in common?如何基于一条共同的线连接线?
【发布时间】:2021-12-22 01:03:43
【问题描述】:

我有一个制表符分隔的文件,如下所示:

4S2P_1:A    4S2P_1:A
4S2P_1:A    6PXX_1:A
4S2P_1:A    6HB8_1:A
4S2P_1:A    6HOO_1:A
4S2P_1:A    6I5D_1:A
4S2R_1:A    4S2R_1:A
4S2C_1:A    4S2C_1:A
4S2C_1:A    4S2B_1:A
4S2E_1:A    4S2E_1:A
4S2E_1:A    5XB5_1:A
4S2E_1:A    5XBH_1:A

创建文件后,第二列中的序列与第一列中的序列相似。 4S2P_1:A 与自身相似,6Q5B_1:A 和 6PXX_1:A 和 6HB8_1:A 等等。 4S2R_1:A 与自身相似。

我想将文件解析为如下所示:

4S2P_1:A 6PXX_1:A 6HB8_1:A 6HOO_1:A 6I5D_1:A
4S2E_1:A 5XB5_1:A 5XBH_1:A
4S2C_1:A 4S2B_1:A
4S2R_1:A

所以我希望输出的第一列和链接到它的列在一行上用空格隔开,并使形成的簇按降序排列。

我想使用 awk 来执行此操作。

我试过用这个:

awk -F '\t' '{print $1*" "$2}' 

但它给了我这个输出:

04S2P_1:A
05DTT_1:A
07ASS_1:A
07AUX_1:A
05HAQ_1:A
05HAP_1:A
05HAR_1:A

它在开头添加一个 0,并且不会将相似的序列保持在同一行。

【问题讨论】:

  • 欢迎来到 Stack Overflow (SO)。 SO is a question and answer page for professional and enthusiast programmers。请在您的问题中添加您自己的代码。您应该至少展示自己为解决这个问题所做的研究。
  • 您为什么要为此使用awk?从技术上讲,您可以在阅读文件时将" " $2 附加到some_array[$1]。但同样的事情可以直接在 Bash 中使用关联数组来实现,即declare -A some_array 等。
  • 如果你想用awk而不是bash来解决它,你应该用“awk”标记你的问题。
  • 文件是否已经按第一列排序?除了按字段数对输出进行排序外,对于输出是否还有其他排序要求......无论是在同一行的字段之间,还是在具有相同字段数的行之间?您最大的输入文件有多大(MBytes?行数?)

标签: bash awk


【解决方案1】:

通常使用哈希来使列表唯一。

#! /bin/bash

declare -A hash

while read -r c1 c2; do
  hash[$c1]+=$'\t'"$c2"
done

for key in "${!hash[@]}"; do
  printf '%s%s\n' "$key" "${hash[$key]}"
done

缺点是,你失去了原来的排序顺序。但在我看来,您并不关心原始订单。如果您想按每行的长度对输出进行排序,您可以选择question 的答案之一。

【讨论】:

    【解决方案2】:

    这是一个简单的 Awk 脚本,用于将具有相同键的值提升到同一行。

    awk '$1 != prev { if(prev) printf "\n";
         prev=$1; printf "%s", $2; next }
       { printf " %s", $2 }
      END { if (prev) printf "\n" }' file
    

    要按每条记录的长度排序,您需要在阅读时将内容保存在内存中。上面的内容因其简单性和健壮性而吸引人(应该适用于任何大小的文件),但我们可以让在每行前面打印一个排序键的过程更加复杂,代价是需要将每条完整的记录保存在记忆直到我们知道它的长度。

    awk 'function pr () { printf "%i\t", n; printf "%s", a[1];
        for(i=2; i<=n; ++i) printf " %s", a[i];
        printf "\n"; delete a; n=0 }
      $1 != prev { if (prev) pr(); prev=$1; a[1]=$2; n=1; next }
      { a[++n] = $2 }
      END { if (n) pr() }' file |
    sort -t $'\t' -k1rn |
    cut -f2-
    

    【讨论】:

    • 或者我们可以将所有内容都保存在内存中,删除第一列中的键被分组的假设并保持脚本简单:awk '{keys[$1] = keys[$1] " " $2} END {for (key in keys) print key, substr(keys[key], 2)}'
    猜你喜欢
    • 1970-01-01
    • 2023-03-15
    • 2011-04-28
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 2022-01-14
    • 1970-01-01
    相关资源
    最近更新 更多