grep变量并提供信息输出

时间:2013-01-26 10:30:19

标签: perl bash sed awk grep

我想查看文件/行中提到的特定单词的次数。

我的虚拟示例如下所示:

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个字母,所以我无法创建像这个单词一样的文件。

5 个答案:

答案 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