我想查看文件/行中提到的特定单词的次数。
我的虚拟示例如下所示:
cat words
blue
red
green
yellow
cat text
TEXTTEXTblueTEXTTEXTblue
TEXTTEXTgreenblueTEXTTEXT
TEXTTEXyeowTTEXTTEXTTEXT
我这样做:
for i in $(cat words); do grep "$i" text | wc >> output; done
cat output
2 2 51
0 0 0
1 1 26
0 0 0
但我真正想得的是:
1.用作变量的词;
2.找到了多少行(除了文本命中)。
优先输出如下:
blue 3 2
red 0 0
green 1 1
yellow 0 0
$ 1 - grep'ed的变量
$ 2 - 在文本中找到变量的次数
$ 3 - 发现了多少行变量
希望有人可以用grep,awk,sed帮助我这样做,因为它们对于大型数据集来说足够快,但是Perl one liner也会帮助我。
修改
试过这个
for i in $(cat words); do grep "$i" text > out_${i}; done && wc out*
它看起来不错,但有些单词超过300个字母,所以我无法创建像这个单词一样的文件。
答案 0 :(得分:4)
您可以使用grep
option -o
仅打印匹配行的匹配部分,在单独的输出行上匹配
while IFS= read -r line; do
wordcount=$(grep -o "$line" text | wc -l)
linecount=$(grep -c "$line" text)
echo $line $wordcount $linecount
done < words | column -t
你可以将它全部放在一行中,使其成为一个衬垫。
如果列给出“列太长”错误,则可以使用printf,前提是您知道最大字符数。使用以下代替echo
并删除管道到列:
printf "%-20s %-2s %-2s\n" "$line" $wordcount $linecount
如果需要,请将20替换为最大字长和其他数字。
答案 1 :(得分:3)
这是一个类似的Perl解决方案;而是写成一个完整的脚本。
#!/usr/bin/perl
use 5.012;
die "USAGE: $0 wordlist.txt [text-to-search.txt]\n" unless @ARGV;
my $wordsfile = shift @ARGV;
my @wordlist = do {
open my $words_fh, "<", $wordsfile or die "Can't open $wordsfile: $!";
map {chomp; length() ? $_ : ()} <$words_fh>;
};
my %words;
while (<>) {
for my $word (@wordlist) {
my $cnt = 0;
$cnt++ for /\Q$word\E/g;
$words{$word}[0] += $cnt;
$words{$word}[1] += 1&!! $cnt; # trick to force 1 or 0.
}
}
# sorts output after frequency. remove `sort {...}` to get unsorted output.
for my $key (sort {$words{$b}->[0] <=> $words{$a}->[0] or $a cmp $b} keys %words) {
say join "\t", $key, @{ $words{$key} };
}
示例输出:
blue 3 2
green 1 1
red 0 0
yellow 0 0
优于bash脚本:每个文件只读一次。
答案 2 :(得分:1)
这变得非常难看,因为Perl单行(部分是因为它需要从两个文件中获取数据,并且只能在stdin上发送一个,部分原因是需要计算匹配的行数和总数比赛),但你去了:
perl -E 'undef $|; open $w, "<", "words"; @w=<$w>; chomp @w; $r{$_}=[0,{}] for @w; my $re = join "|", @w; while(<>) { $l++; while (/($re)/g) { $r{$1}[0]++; $r{$1}[1]{$l}++; } }; say "$_\t$r{$_}[0]\t" . scalar keys %{$r{$_}[1]} for @w' < text
这需要perl 5.10或更高版本,但将其更改为支持5.8及更早版本是微不足道的。 (将-E
更改为-e
,将say
更改为print
,并在每行输出的末尾添加\n
。)
输出:
blue 3 2
red 0 0
green 1 1
yellow 0 0
答案 3 :(得分:1)
awk(gawk)oneliner可以帮助你避免grep拼图:
awk 'NR==FNR{n[$0];l[$0];next;}{for(w in n){ s=$0;t=gsub(w,"#",s); n[w]+=t;l[w]+=t>0?1:0;}}END{for(x in n)print x,n[x],l[x]}' words text
稍微格式化代码:
awk 'NR==FNR{n[$0];l[$0];next;}
{for(w in n){ s=$0;
t=gsub(w,"#",s);
n[w]+=t;l[w]+=t>0?1:0;}
}END{for(x in n)print x,n[x],l[x]}' words text
用你的例子测试:
kent$ awk 'NR==FNR{n[$0];l[$0];next;}{for(w in n){ s=$0;t=gsub(w,"#",s); n[w]+=t;l[w]+=t>0?1:0;}}END{for(x in n)print x,n[x],l[x]}' words text
yellow 0 0
red 0 0
green 1 1
blue 3 2
如果要格式化输出,可以将awk输出传递给column -t
所以它看起来像:
yellow 0 0
red 0 0
green 1 1
blue 3 2
答案 4 :(得分:1)
awk '
NR==FNR { words[$0]; next }
{
for (word in words) {
count = gsub(word,word)
if (count) {
counts[word] += count
lines[word]++
}
}
}
END { for (word in words) printf "%s %d %d\n", word, counts[word], lines[word] }
' file