我的目录中有几个包含此类内容的文件:
Wood *
Nails
Large Hammer *
有些名字后面有一个星号,有些则没有。我有多个包含此类内容的文件。在每个文件中,产品可能有也可能没有单身的旁边有星星。我需要制作一个 bash 脚本来计算所有文件中每个产品的星号出现次数。例如,输出需要如下所示:
Wood 12
Yellow Lamps 6
Nails 4
...
这意味着在所有文件中,它在木材旁边发现了 12 颗星星,在灯旁边发现了 6 颗星星,等等......
用 C 语言解析它非常容易,但我不想运行二进制文件。我想要一个 shell 脚本,但我不太擅长使用 grep 和 awk,但我确信我需要这些。
我知道如何计算星星本身,但我不确定如何跟踪哪个星星属于哪个产品。
答案1
像这样,与一个awk:
awk '$NF=="*"{$NF=""; arr[$0]++}END{for (i in arr) print i arr[i]}' ./*
$NF
默认情况下是最新的字符串,以空格分隔- 主要技巧是创建一个名为
arr
ay 的关联词,其中当前单词为钥匙并递增为价值 - 在
END
我们迭代每个键/值的arr
ayprint
和珀尔单行:
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