计算以“*”结尾的行数

计算以“*”结尾的行数

我的目录中有几个包含此类内容的文件:

Wood *
Nails
Large Hammer *

有些名字后面有一个星号,有些则没有。我有多个包含此类内容的文件。在每个文件中,产品可能有也可能没有单身的旁边有星星。我需要制作一个 bash 脚本来计算所有文件中每个产品的星号出现次数。例如,输出需要如下所示:

Wood 12
Yellow Lamps 6
Nails 4
...

这意味着在所有文件中,它在木材旁边发现了 12 颗星星,在灯旁边发现了 6 颗星星,等等......

用 C 语言解析它非常容易,但我不想运行二进制文件。我想要一个 shell 脚本,但我不太擅长使用 grep 和 awk,但我确信我需要这些。

我知道如何计算星星本身,但我不确定如何跟踪哪个星星属于哪个产品。

答案1

像这样,与一个

awk '$NF=="*"{$NF=""; arr[$0]++}END{for (i in arr) print i arr[i]}' ./*
  • $NF默认情况下是最新的字符串,以空格分隔
  • 主要技巧是创建一个名为array 的关联词,其中当前单词为钥匙并递增为价值
  • END我们迭代每个键/值的arrayprint

单行:

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我按照您想要的每个单词出现的次数为行添加前缀。

sort命令很重要,如果不使用它,预期的结果会不同

根据上面的输出,最终输出(使用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

我用下面的两个输入文件进行了测试(vim 显示);第一个来自埃德加·马加隆的回答:

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

相关内容