grep,awd用于获取唯一值的文件,然后计算结果

时间:2016-02-29 16:59:07

标签: awk sed grep

我有一个包含100,000行xml事务的巨大日志

许多行包含重复的条目,例如帐户ID我想grep / sed或awk这些帐户ID的排序并显示唯一结果或计算它。

以下是我试图grep / sed / awk的模式

<Account Id="123456789012">

到目前为止,我已经尝试了以下内容:

sort 20150229.log | grep '<Account Id="*">' | uniq | wc -l

但我得到0结果......

请告知

感谢

4 个答案:

答案 0 :(得分:0)

计算文本文件中的唯一行

我有这样的别名,因为我经常遇到它:

alias cnt='sort -if |uniq -ic |sort -ifn'  # case insensitive
alias CNT='sort |uniq -c |sort -n'         # strict, case sensitive

这会对输入进行排序(-i忽略非打印字符,-f忽略大小写)然后使用uniq(只能处理预先排序的数据,-i就是这种情况不敏感,-c计算重复次数),然后按数字排序(-n为数字)。 (注意:cnt输出的最终案例可能比预期更大写,因为命令如何纠正案例差异。)

调用它:

cat 20150229.log |cnt

cnt的参数将传递给最终sort命令,因此您可以使用-r之类的标志来反转排序。我建议通过tailawk '$1 > 5'之类的方式运行它,以消除所有小条目。

解析XML

以上内容适用于日志等随机文本文件。解析HTML或XML是Bad Idea™,除非您完全了解要解析的确切格式。

也就是说,你有一个grep查询,其中有一个有缺陷的正则表达式来匹配XML:

grep '<Account Id="*">'

这匹配<Account Id="">(以及您可能不需要的<Account Id="><Account Id=""">),但它与您的示例<Account Id="123456789012">不匹配。该正则表达式中的*查找前一个字符(")中的零个或多个。这是a more thorough explanation

您需要.代表任何字符(explanation here):

grep '<Account Id=".*">'

此外,除非你给它grep标志,否则-x将不匹配实线,我猜你不想要它,因为如果周围有空格,它将会失败(看到上面的Bad Idea™链接!)。这是grep的一个更便宜的版本,利用我的别名:

grep '<Account Id=' 20150229.log |cnt

答案 1 :(得分:0)

使用解析器非常容易。我喜欢XML::Twig这类工作,因为你可以随时清除。

但是像:

#!/usr/bin/env perl
use strict;
use warnings;

my %count_of;

sub count_unique_id {
    my ( $twig, $account ) = @_;
    my $id = $account->att('id'); 
    print "New ID: $id\n" unless $count_of{$id};
    $count_of{$id}++;
    $twig -> purge; 
}

my $twig = XML::Twig -> new ( twig_handlers => { 'Account' => \&count_unique_id } );
$twig -> parsefile ( 'your_file.xml'); 

foreach my $id ( keys %count_of ) { 
   print "$id => $count_of{$id}\n";
}

print "There were ", scalar keys %count_of, " unique IDs\n"; 

答案 2 :(得分:0)

如果您对XML的规律性有信心并且不觉得需要使用XML感知工具,那么以下可能就足够了,并且具有一些优点,例如:它不需要gawk,同时仍然可以容忍小的变化:

awk -v RS='<' '/^Account +Id *=/ { sub(/^[^=]*= *"/,""); sub(/".*/, ""); print}' |
sort | uniq

如果您想避免排序,那么您可以轻松修改awk脚本,例如如下:

awk -v RS='<' '
 /^Account +Id *=/ { sub(/^[^=]*= *"/,""); sub(/".*/, ""); m[$0]}
 END {for (i in m) {print i}}' 

答案 3 :(得分:0)

您还没有向我们展示任何可测试的样本输入和预期输出,所以它可以猜测,但这可能是您想要的:

awk 'sub(/.*<Account Id="/,"") && sub(/".*/,"") && !seen[$0]++' 20150229.log