7

我有一个包含三列的制表符分隔文件(摘录):

AC147602.5_FG004    IPR000146   Fructose-1,6-bisphosphatase class 1/Sedoheputulose-1,7-bisphosphatase
AC147602.5_FG004    IPR023079   Sedoheptulose-1,7-bisphosphatase
AC148152.3_FG001    IPR002110   Ankyrin repeat
AC148152.3_FG001    IPR026961   PGG domain

我想用 bash 得到这个:

AC147602.5_FG004 IPR000146 Fructose-1,6-bisphosphatase class 1/Sedoheputulose-1,7-bisphosphatase IPR023079 Sedoheptulose-1,7-bisphosphatase
AC148152.3_FG001 IPR023079 Sedoheptulose-1,7-bisphosphatase IPR002110   Ankyrin repeat IPR026961    PGG domain

因此,如果第一列中的 ID 在多行中相同,则应该为每个 ID 生成一行,并将所有其他部分的行连接起来。在示例中,它将提供两行文件。

4

4 回答 4

9

试试这个单线:

 awk -F'\t' -v OFS='\t' '{x=$1;$1="";a[x]=a[x]$0}END{for(x in a)print x,a[x]}' file
于 2013-11-06T22:37:05.873 回答
0

无论出于何种原因,awk 解决方案在 cygwin 中对我不起作用。所以我改用 Perl。它围绕一个制表符连接并用 \n 分隔行

cat FILENAME | perl -e 'foreach $Line (<STDIN>) { @Cols=($Line=~/^\s*(\d+)\s*(.*?)\s*$/); push(@{$Link{$Cols[0]}}, $Cols[1]); } foreach $List (values %Link) { print join("\t", @{$List})."\n"; }'
于 2017-01-20T14:03:08.707 回答
0

将取决于文件大小(和 awk 限制)

如果太大,这将通过首先对文件进行排序来减少对 awk 的需求,并且只在内存中保留 1 个标签用于打印

使用整行修改后的经典版本

sort YourFile \
 | awk '
      last==$1 { sub( /^[^[:blank:]]*[[:blank:]]+/, ""); C = C " " $0; next}
      NR > 1 { print Last C; Last = $1; C = ""}
      END { print Last}
      '

另一个使用字段和预印本但不太“人类可读”的版本

sort YourFile \
 | awk '
      last!=$1 {printf( "%s%s", (! NR ? "\n" : ""), Last=$1)}
      last==$1 {for( i=2;i<NF;i++) printf( " %s", $i)}
      '
于 2017-01-20T14:30:18.367 回答
0

纯 bash 版本。它没有其他依赖项,但需要 bash 4.0 或更高版本 (2009) 才能支持关联数组。

全部在一条线上:

{ declare -A merged; merged=(); while IFS=$'\t' read -r key value; do merged[$key]="${merged[$key]}"$'\t'"$value"; done; for key in "${!merged[@]}"; do echo "$key${merged[$key]}"; done } < INPUT_FILE.tsv

可读和评论的等价物:

{
  # Define `merged` as an empty associative array.
  declare -A merged
  merged=()

  # Read tab-separated lines. Any leftover fields also end up in `value`.
  while IFS=$'\t' read -r key value
  do
    # Append to any value that's already there, separated by a tab.
    merged[$key]="${merged[$key]}"$'\t'"$value"
  done

  # Loop over the input keys. Note that the order is arbitrary;
  # pipe through `sort` if you want a predictable order.
  for key in "${!merged[@]}"
  do
    # Each value is prefixed with a tab, so no need for a tab here.
    echo "$key${merged[$key]}"
  done
} < INPUT_FILE.tsv
于 2021-07-01T07:32:47.077 回答