2013-08-28 45 views
1

快速簡潔地匹配來自具有匹配第一個字段的文本文件的行的方式。用於匹配具有匹配第一個字段的行的命令行(sed,awk等)

樣品輸入:

a|lorem 
b|ipsum 
b|dolor 
c|sit 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

所需的輸出:

b|ipsum 
b|dolor 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

所需的輸出,可供選擇:

b|ipsum|dolor 
d|amet|consectetur 
e|adipisicing|elit 

我可以想像很多方法來寫這篇文章,但我懷疑有一個聰明的方式來做到這一點,例如,使用sed,awk等。我的源文件大約爲0.5 GB。

這裏有一些相關的問題,例如「awk | merge line on the basis of field matching」,但是其他問題將過多的內容加載到內存中。我需要一個流媒體方法。

+6

解釋爲什麼這是所需的輸出,因爲它根本不明顯。例如你是否在尋找一個能讓你指定b,d和e作爲所需鍵值的工具,或者你在尋找鍵盤在輸入中出現兩次還是其他的情況? –

+0

我想合併行匹配的第一個字段。對不起,這不清楚。此外,輸入是排序的。 –

回答

3

這裏有一個方法,你只需要記住前一行(因此需要輸入要排序的文件)

awk -F \| ' 
    $1 == prev_key {print prev_line; matches ++} 
    $1 != prev_key {        
     if (matches) print prev_line 
     matches = 0 
     prev_key = $1 
    }     
    {prev_line = $0} 
    END { if (matches) print $0 } 
' filename 
b|ipsum 
b|dolor 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

備用輸出

awk -F \| ' 
    $1 == prev_key { 
     if (matches == 0) printf "%s", $1 
     printf "%s%s", FS, prev_value 
     matches ++ 
    }    
    $1 != prev_key { 
     if (matches) printf "%s%s\n", FS, prev_value 
     matches = 0         
     prev_key = $1 
    }     
    {prev_value = $2} 
    END {if (matches) printf "%s%s\n", FS, $2} 
' filename 
b|ipsum|dolor 
d|amet|consectetur 
e|adipisicing|elit 
+0

但是OP怎麼能得到'Desired output ,替代? – anubhava

+0

您的方法很好,0m16.330s處理。時間awk -F \ | '$ 1 == prev_key {print prev_line;匹配++} $ 1!= prev_key {if(matches)print prev_line;匹配= 0; prev_key = $ 1; } {prev_line = $ 0} END {if(matches)print $ 0}'INFILE> OUTFILE –

3

對於固定寬度的字段,你可以使用uniq

$ uniq -Dw 1 file 
b|ipsum 
b|dolor 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

這裏如果你沒有固定的寬域兩種awk解決方案:

awk -F'|' '{a[$1]++;b[$1]=(b[$1])?b[$1]RS$0:$0}END{for(k in a)if(a[k]>1)print b[k]}' file 
b|ipsum 
b|dolor 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

awk -F'|' '{a[$1]++;b[$1]=b[$1]FS$2}END{for(k in a)if(a[k]>1)print k b[k]}' file 
b|ipsum|dolor 
d|amet|consectetur 
e|adipisicing|elit 
+0

謝謝。第二個領域是不可預知的長度,通常> 100個字符。順便說一句,那些「uniq」的參數在MacOS和Ubuntu中都不可用。 –

+0

夠公平的,這兩個'awk'腳本應該爲你做好訣竅。你確定它們不能在你的Ubuntu機器上使用嗎?你有什麼版本的coreutils。 'uniq --version - uniq(GNU coreutils)8.21' –

+0

謝謝!第二個是我真正需要的。你的方法運作良好;第一個爲0m29.103s,第二個爲0m34.036s。 –

1

用awk:

awk -F '|' '!($1 in a){a[$1]=$2; next} $1 in a{b[$1]=b[$1] FS a[$1] FS $2} 
    END{for(i in b) print i b[i]}' file 
d|amet|consectetur 
e|adipisicing|elit 
b|ipsum|dolor 
+4

當輸入文件很大時會有很高的內存需求 –

+0

我對awk的擔心是將所有內容加載到內存中,然後在END中調用它;但我的擔憂可能是沒有根據的。我會試試這個。謝謝! –

+1

令我驚訝的是,您的方法適用於我的0.5GB輸入文件。處理時間0m19.184s。時間awk -F'|' '!(a中的$ 1){a [$ 1] = $ 2;下一步}在{b [$ 1] = b [$ 1] FS a [$ 1] FS $ 2} $ {1} END {for(i in b)print ib [i]}'INFILE> OUTFILE –

0
$ awk -F'|' '$1 == prev {rec = rec RS $0; size++; next} {if (size>1) print rec; rec=$0; size=1} {prev = $1} END{if (size>1) print rec}' file 
b|ipsum 
b|dolor 
d|amet 
d|consectetur 
e|adipisicing 
e|elit 

$ awk -F'|' '$1 == prev {rec = rec FS $2; size++; next} {if (size>1) print rec; rec=$0; size=1} {prev = $1} END{if (size>1) print rec}' file 
b|ipsum|dolor 
d|amet|consectetur 
e|adipisicing|elit 
+0

我試過了你的第二種方法。這很快,但我得到了一些虛假的點擊。謝謝你的樣品。 –

+0

錯誤點擊?很難相信,如果您的實際輸入看起來像您的示例輸入,但如果您想分享您的輸入以及您獲得的不良輸出,我很樂意看一看。 –

+0

埃德,我不是故意批評,而且錯誤可能在我身邊。我只能說,當我進行快速測試時,輸出結果並不符合我的預期。我的輸入實際上比我給出的樣本複雜得多,但仍然基本上是由管道分隔的兩個字段的相同想法。我認爲沒有必要深入研究這一點。再次感謝。 –

1

這可能適合你(GNU sed):

sed -r ':a;$!N;s/^(([^|]*\|).*)\n\2/\1|/;ta;/^([^\n|]*\|){2,}/P;D' /file 

這會在模式空間中讀取2行,然後檢查兩行中的鍵是否相同。如果是這樣,它刪除第二個鍵並重復。如果不是,它將檢查第一行是否存在兩個以上的字段,如果是,則將其打印出來,然後刪除它,否則只刪除第一行。

+0

感謝您的支持。我已經使用過awk,但是它有一個sed解決方案。 –

+0

注意,在mac上,「sed -r」是「sed -E」;還請注意,您的方法對我來說並不適用,至少在我的Mac上,具有上述測試內容。 –