回答:
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1
最初のawkフィルターは、というファイルの各行の長さを出力するだけですfile。このファイルには1行に1ワードが含まれていると想定しています。
sort -n(の出力からソート行awk昇順に数値)とuniq -c(各行が連続発生回数をカウント)を与えられたデータのためのものから次の出力を作成します。
3 2
1 5
1 7
次に、これは、awk各行を「X文字のY文字を持つ行」と解釈する2番目のスクリプトによって解析され、必要な出力が生成されます。
代替の解決策は、それをすべて行いawk、長さの数を配列に保持することです。効率、読みやすさ、理解しやすさ(したがって保守性)の間のトレードオフであり、どのソリューションが「最適」か。
代替ソリューション:
$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1
すべてでそれを行う別の方法awkだけでは
$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt
2 character words - 3
5 character words - 1
7 character words - 1
words[length()]++ 入力行の長さをキーとして使用してカウントを保存するEND{for(k in words)print k " character words - " words[k]} すべての行が処理された後、配列の内容を目的の形式で出力します
パフォーマンスの比較、選択された数値は2つの実行のうち最良
$ wc words.txt
71813 71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M long_file.txt
$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1
real 0m20.632s
user 0m20.464s
sys 0m0.108s
$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2
real 0m19.749s
user 0m19.640s
sys 0m0.108s
$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3
real 1m23.294s
user 1m24.952s
sys 0m1.980s
$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical
ファイルにASCII文字のみが含まれる場合、
$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1
real 0m15.651s
user 0m15.496s
sys 0m0.120s
時間perlがあまり変わらなかった理由がわからない。おそらくエンコードを他の方法で設定する必要がある
lengthなしで()完全に罰金ここ作品なので、中括弧を追加するために冗長かもしれません。ただし、GNU awkを使用しています。
In older versions of awk, the length() function could be called without any parentheses. Doing so is considered poor practice, although the 2008 POSIX standard explicitly allows it, to support historical practice. For programs to be maximally portable, always supply the parentheses
以下はperl同等のものです(with -optional-sort):
$ perl -lne '
$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1
{$a<=>$b}後に追加するsortと、修正されます。あるいは、数値キーを持つ通常の配列を使用して、値がゼロまたは未定義のキーをスキップすることもできます。
代替1つの使用してのGNU AWKの呼び出し、printf関数を:
$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
{c[length($0)]++}
END{
for(i in c){printf("%s character words - %s\n",i,c[i])}
}' infile
2 character words - 3
5 character words - 1
7 character words - 1
コアアルゴリズムは、配列内の文字数を収集するだけです。最後の部分は、printfでフォーマットされた収集されたカウントを出力します。
awkへの高速でシンプルな1回の呼び出し。
正確には、配列を維持するためにさらに多くのメモリが使用されます。
しかし、ソートは呼び出されず(数値配列のインデックスは、PROCINFOで常に上向きにソートされるようにトラバースされるように設定されています)、複数ではなく1つの外部プログラムのみawkです。
for in少なくともいくつかの値またはいくつかのawk実装では、数値配列インデックスを数値順に与える場合がありますが、これは必須ではなく、伝統的ではなく、普遍的ではありません。2、3、または4のような小さなセットでよく発生します。(gawkでPROCINFOまたはWHINY_USERSを使用せずに)アクセスできるすべてのawkで10または20を試してください。
@ind_str_asc文字列として並べ替えます。これは、数値がすべて1桁の場合にのみ適切です(例のように)。@ind_num_asc(任意の)値が10以上の場合に使用します。また、以前ほど問題ではなくなっていますが、この機能はgawk 4.0以降のみです。