2016-02-29 17 views
0

我有一個包含行XML交易100,000s的grep,sed的AWK的獨特值的文件再算上結果

多條線路包含重複的條目如帳戶ID的,我想用grep/SED或一個巨大的日誌awk這些帳戶ID的排序,並顯示獨特的結果或計數。

下面是我想要的圖案的grep/SED/AWK

<Account Id="123456789012"> 

到目前爲止,我已經試過如下:

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

,但我得到0的結果....

請告知

感謝

+1

編輯您的問題,包括一些簡潔的,可測試樣本輸入和期望的輸出,所以我們可以幫助你,但一目瞭然 - '*'意味着'前面的正則表達式段的零或更多的重複',你應該使用'[^「] *'而不是'*'。還有其他的 –

+0

使用XML/HTML解析器(xmllint,xmlstarlet ...)。 – Cyrus

+0

'sort'本身可以爲你生成唯一的行,嘗試'grep -E' dawg

回答

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這樣的標誌來反轉排序。我建議通過tail或類似awk '$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=".*">' 

此外,除非你給它-x標誌grep不會匹配全行,我猜你不」不要這樣做,因爲如果有周圍的空白,它會失敗(參見上面的Bad Idea™鏈接!)。這是一個更便宜的grep版本,利用我的別名:

grep '<Account Id=' 20150229.log |cnt 
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"; 
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}}' 
0

您還沒有我們表現出任何檢驗的樣品的輸入和預期的輸出,所以這是一個猜測,但是這可能是你想要什麼:

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