「*」で終わる行数の計算

「*」で終わる行数の計算

私のディレクトリには、次の内容を含むいくつかのファイルがあります。

Wood *
Nails
Large Hammer *

いくつかの名前は後ろにアスタリスクがあり、一部はそうではありません。この種類のコンテンツを含む複数のファイルがあります。各ファイルには製品がある場合とない場合があります。一つ隣には星があります。すべてのファイルで各製品のアスタリスクが発生した回数を数えるbashスクリプトを作成する必要があります。たとえば、出力は次のようになります。

Wood 12
Yellow Lamps 6
Nails 4
...

これは、すべてのファイルで木の横に星12個、ランプの横に星6個などが見つかったことを意味します。

Cで解析するのは非常に簡単ですが、バイナリを実行したくありません。私はシェルスクリプトが欲しいが、grepとawkに精通していませんが、これが必要だと確信しています。

星自体を数える方法は知っていますが、どの星がどの製品に属しているかをどのように追跡するのかわかりません。

答え1

このように:

awk '$NF=="*"{$NF=""; arr[$0]++}END{for (i in arr) print i arr[i]}' ./*
  • $NFデフォルトはスペースで区切られた最新の文字列です。
  • 主な秘訣は、arr現在単語がある場所にayという関連単語を作成することです。そして増加
  • ああ、私たちENDは各キー/値を繰り返します。arrprint

そして1本のライン:

perl -anE '
    if ($F[-1] eq "*") {
        $k = join " ", @F[0..@F-2];
        $a->{$k}++
    }
    END{say "$_ $a->{$_}" for keys %$a}
' ./*

-aはい分ける@F基本配列のパターン

答え2

次のことができます。

sed -n 's/[[:blank:]]*\*$//p' ./* |
  LC_ALL=C sort |
  LC_ALL=C uniq -c |
  sort -rn

<blanks>*末尾の行を削除し(pそのような代替項目を含む行のみを印刷します)、sort | uniq -c一意の行数を計算するために使用されます(Cロケールではバイト間比較です)。

答え3

これがパフォーマンスに影響を与えるかどうかはわかりません。 (非常に大きなファイルがある場合は、このコマンドは遅くなければならないと思います):

grep -Fh '*' | tr -s ' ' | sort | uniq -c

携帯性が向上しました。

grep -Fh '*' * 2>/dev/null | tr -s ' ' | sort | uniq -c

サブディレクトリに検索するファイルがさらに含まれている場合:

grep -Fh '*' **/* 2>/dev/null | tr -s ' ' | sort | uniq -c | sed 's/.$//'

または以下を使用しないでください2>/dev/null

find . -type f -exec grep -Fh '*' {} + | tr -s ' ' | sort | uniq -c | sed 's/.$//'

この部分は、grep -Fh '*'最後にあるすべての行が一致することを意味します。パターンと一致するファイル名の印刷を抑制し、リテラル文字列を使用するときに使用されます(「*」はパターンではなく文字列として機能します)。たとえば、各行間の重複スペースを削除しています。*-h-F
tr -s ' '

Need *
Word   buzz *
Need *
More   *
More *
Word   *
More   *
More *
Word   *
Word   *
Need *
More *

このtrコマンドは次のように解析します。

Need *
Word buzz *
Need *
More *
More *
Word *
More *
More *
Word *
Word *
Need *
More *

上記は、次の出力を得るためにパイプされますsort

More *
More *
More *
More *
More *
Need *
Need *
Need *
Word *
Word *
Word *
Word buzz *

最後に、uniq -c必要な各単語の発生回数を行の前に付けます。

ソートコマンドは重要です。使用しないと、予想される結果が異なります。

上記の出力に基づいて、最終出力(使用済みuniq -c)は次のとおりです。

5 More *
3 Need *
3 Word *
1 Word buzz *

削除するには、パイプを介して最後の文字を削除するか、次のように*します。sed*

grep -Fh '*'  * | tr -s ' ' | sort | uniq -c | sed 's/.$//'
#or
grep -Fh '*' * | tr -s ' ' | sort | uniq -c | sed 's/\*//'

ここでは、目的の出力を取得するために複数のコマンドを使用したので、これを達成するためのより良い方法があると思います。前述したように、これはパフォーマンスを低下させる可能性があります。

答え4

bashまたはawkを使用することをお勧めしますが、(GNU)sedでこれを行うことをお勧めします。

s:  *: :g
/\*$/!s:$: :
G
s:([^\n]+) (\*?)(.*\n)\1 (\**)\n:\3\1 \4\2\n:
s:^\n::
h;$!d
s:\n$::
:u2d
    s:\*:<<123456789*01>:m
    s:(.)<.*\1(\**.).*>:\2:m
tu2d

次の2つの入力ファイル(vimディスプレイ)を使用してテストしました。最初は、Edgar Magalonの答えから来たものです。

Need *         |Need
Word   buzz *  |Word   buzz
Need *         |Need
More   *       |More *
More *         |More *
Word   *       |Word
More   *       |More *
More *         |More *
Word   *       |Word
Word   *       |Word
Need *         |Need
More *         |More *
~              |~
~              |~
input1          input2

結果:

~$ sed -rf script.sed input1 input2
Word 3
More 10
Word buzz 1
Need 3

関連情報