awk:最初の列に基づいてソートし、2番目の列のみを出力し、2番目の列をすべて出力します。

awk:最初の列に基づいてソートし、2番目の列のみを出力し、2番目の列をすべて出力します。

次のように作成できる2つの列ファイルがあります。

cat > twocol << EOF
007 03
001 03
003 01
137 12
001 11
002 01
002 02
002 03
001 02
002 04
137 94
010 21
001 01
EOF

生成されたファイルにはtwocol垂直線のみが含まれています。


希望する結果

twocolある種のコマンドを実行し、次の結果を得たいと思います。 (私はもう少し混乱している質問のタイトルをもう一度説明するよりもはるかに優れていると思います。」出力します。」

001 01
    02
    03
    11
002 01
    02
    03
    04
003 01
007 03
010 21
137 12
    94

これはsort単純なものが私に提供するものとは異なります。

001 01
001 02
001 03
001 11
002 01
002 02
002 03
002 04
003 01
007 03
010 21
137 12
137 94

私は働く

私が考えることができる唯一の解決策は、(まともなスクリプトを取得する前に)私が思いついた最初のソリューションですawk。いくつかのインスタンスawk、群れ、およびbash以下の助けを使用して、上に太字で示されている望ましい結果と一致します。1

col_1_max_len=$(awk '
  BEGIN{maxl=0;}
  {curr=length($1);max1=max1>curr?max1:curr;}
  END{print max1}' \
 twocol);
len1=$col_1_max_len;
len2=$(awk '
  BEGIN{max2=0;}
  {curr=length($2);max2=max2>curr?max2:curr;}
  END{print max2}' \
 twocol);
current_col_1_val="nothing";

while read -r line; do {
  current_row="${line}";
  col_1_val=$(awk '{print $1}' <<< "${current_row}");
  col_2_val=$(awk '{print $2}' <<< "${current_row}");
  if [ ! "${col_1_val}" == "${current_col_1_val}" ]; then
    printf "%0"$len1"d %0"$len2"d\n"  "${col_1_val}"  "${col_2_val}";
  else
    printf "%"$len1"s %0"$len2"d\n"  " "  "${col_2_val}";
  fi;
}; done < <(sort twocol)

awkこの答えと同様に、単一パスを使用できる必要があると思います。2サム45、...

さらに、かさばり、メモリを大量に消費する配列なしでは一緒に使用できないようです。この形式はまた私に問題を提起します。 1列目と2列目の数字は、より多くの桁数を持つことができ、好ましくは良く見える。

誰でも良い方法でこの結果を得る方法を教えてもらえますか? awk コード - 端末で簡単に使用できることが望ましいですか? Perl 答えも大歓迎です。


妻のシステム

$ uname -a && bash --version | head -1 && awk --version | head -1
CYGWIN_NT-10.0 MY-MACHINE 3.2.0(0.340/5/3) 2021-03-29 08:42 x86_64 Cygwin
GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)
GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.2.0-p9, GNU MP 6.2.1)

(FedoraとUbuntuシステムで同じ動作が表示されます。)



編集する

私は解決策を思い出しましたawk。すべてが大丈夫に見え、短いように見えますが、まだ何か間違っているようです。

awk '{if (!vals[$1]++) print($0); else print("   ",$2);}' <(sort twocol)

私は配列で多くのメモリを使用していると思いますvals。現在、私のファイルは約10,000行にすぎませんが、もっと大きくしたいと思います。フォーマットでハードコードしていますが、長さが異なる文字列を持つことができるので気に入らません。

変数を使用して3回実行して変数を渡すと、awkこの形式を変更できます。

length1=$(awk '
  BEGIN{maxl=0;}
  {curr=length($1);max1=max1>curr?max1:curr;}
  END{print max1}' \
 twocol);

length2=$(awk '
  BEGIN{max2=0;}
  {curr=length($2);max2=max2>curr?max2:curr;}
  END{print max2}' \
 twocol);

awk -vlen1=$length1 -vlen2=$length2 '
{
  if (!vals[$1]++) 
    printf("%0*d %0*d\n",len1,$1,len2,$2); 
  else 
    printf("%*s %0*d\n",len1," ",len2,$2);
}' <(sort twocol)

結果は私が望むものとまったく一致していますが(上記の太字を参照)、一度に合格する方法があることを望みましたawk

私が言及した特性に合ったものを共有できる人はいますか?さまざまなアプローチの時間パフォーマンスおよび/またはメモリパフォーマンスに関するコメントも感謝します。

私は並べ替えもできると思いますawk。特にそれがより効率的であるかどうか疑問に思います。編集する:@steeldriverと@markp-fusoが以下に示すようにこれを行うことができます。

ベストアンサー1

元のawkソリューションが削除されました - aより良いソリューション公開済み


実際に入力を事前にソートしてから、awkを使用してフォーマットすることができます。

sort twocol | awk 'BEGIN{OFS="\t"} {print $1 == last ? "" : $1, $2; last = $1}'
001     01
        02
        03
        11
002     01
        02
        03
        04
003     01
007     03
010     21
137     12
        94

これにより、タブ区切りの出力が生成されます。スペースが必要な場合は結果をパイプしますexpand


または、匿名配列のPerlハッシュを使用して2番目の列値を集計し、ソートして印刷することもできます。

perl -alne '
  push @{ $h{$F[0]} }, $F[1] 
  }{ 
  foreach $k (sort {$a <=> $b} keys %h) {
    @a = sort {$a <=> $b} @{ $h{$k} };
    print join "\n", map { ($_ == 0 ? $k : "") . "\t" . $a[$_] } 0..$#a;
  }
  ' twocol
001     01
        02
        03
        11
002     01
        02
        03
        04
003     01
007     03
010     21
137     12
        94

{$a <=> $b}ゼロパディングされたデータを事前にソートすることは、数字でソートするのと同じであるため、これらの操作は不要です。


ただ楽しく、ミラー:

mlr -S --nidx --ofs tab put -q '
  @m[$1] = is_not_array(@m[$1]) ? [$2] : append(@m[$1],$2); 
  end { 
    @m = sort(apply(@m, func(k,v) { return {k: joinv(sort(v), "\n\t")}; }));
    emit @m, ""
  }
  ' twocol 
001     01
        02
        03
        11
002     01
        02
        03
        04
003     01
007     03
010     21
137     12
        94

おすすめ記事