次のように作成できる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、サム、4、5、...
さらに、かさばり、メモリを大量に消費する配列なしでは一緒に使用できないようです。この形式はまた私に問題を提起します。 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