2017-10-13 106 views
4

幾年來,我經常需要將(排序的)文本行與匹配的第一個字段組合起來,並且我從來沒有找到一種優雅的(即單行unix命令行)做到這一點。我想要的與unix join命令的可能性類似,但join需要2個文件,每個鍵最多顯示一次。我想從一個文件開始,其中一個密鑰可能會出現多個圖塊。結合具有匹配第一個字段的行

我有一個ruby和perl腳本來做到這一點,但沒有辦法將我的算法縮短爲一行。經過多年的unix使用,我仍在學習comm,paste,uniq等新的技巧,我懷疑有一個聰明的方法來做到這一點。

還有一些相關的問題,如join all lines that have the same first column to the same line; Command line to match lines with matching first field (sed, awk, etc.);和Combine lines with matching keys - 但這些解決方案從來沒有真正提供一個乾淨和可靠的解決方案。

這裏的樣本輸入:

apple:A fruit 
apple:Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit 
cherry:1 for me to eat 
cherry:bright red 

這裏的示例輸出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 

這裏是我理想中的語法:

merge --inputDelimiter=":" --outputDelimiter=";" --matchfield=1 infile.txt 

的 「matchfield」 實在是可選的。它可能永遠是第一個領域。分隔符的後續顯示應該像純文本一樣對待。

我不介意perl,ruby,awk單行程,如果你能想到一個簡短而優雅的算法。這應該能夠處理數百萬行的輸入。有任何想法嗎?

回答

2

發現AWK語言:

awk -F':' '{ v=substr($0, index($0,":")+1); a[$1]=($1 in a? a[$1]";" : "")v } 
      END{ for(i in a) print i,a[i] }' OFS=':' infile.txt 

輸出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 
+0

感謝@RomanPerekhrest,工作。比過去我嘗試過的其他awk解決方案要好得多,這些解決方案會在複雜的線路上突破。也就是說,我仍然喜歡用簡單的語法來縮短命令,但我很樂意擁有一行代碼。 – MichaelD

1
for F in `cut -f1 -d ':' infile.txt | sort | uniq`; do echo "$F:$(grep $F infile.txt | cut -f2- -d ':' | paste -s -d ';' -)"; done 

不知道它有資格作爲 '高雅',但它的作品,但我敢肯定不會很快對於數百萬行 - 隨着grep調用次數的增加,它會顯着減慢。你期望什麼比例匹配字段是唯一的?

+0

感謝您的unix字符串。我預計重複密鑰/匹配字段的重複次數大約是1-5次,因此在一百萬行中可能會有30萬個密鑰。 – MichaelD

+0

啊,30萬次grep調用是不合理的。感謝您的反饋 – jgrundstad

1

我覺得這個做的工作

awk -F':' '$1!=a{if(b);print b;b=""}a=$1{$1="";if(!b)b=a;b=b$0}END{print b}' infile 
+2

你能解釋一下嗎? – ghoti

3

使用AWK一個襯墊

awk -F: -v ORS="" 'a!=$1{a=$1; $0=RS $0} a==$1{ sub($1":",";") } 1' file 

輸出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 

設置ORS="";默認情況下它是\n
我們設置ORS=""(輸出記錄分隔符)的原因是因爲我們不希望awk在每條記錄末尾的輸出中包含換行符。我們希望通過我們自己的邏輯以我們自己的方式處理它。實際上,我們實際上在每條記錄的開頭都包含換行符,這些記錄的第一個字段與前一個字段不同。

a!=$1:當變量a(最初爲null)與第一個字段$1不匹配時,例如, apple在第一行,然後設置a=$1$0=RS $0,即$0或者簡單地whole record變成"\n"$0(基本上在記錄開始處添加換行符)。當第一個字段($1)比前一行的$1有不同時,a!=$1將始終滿足,因此是根據第一個字段分隔我們記錄的標準。

a==$1:如果它匹配,那麼它可能意味着您正在迭代屬於上一個記錄集的記錄。在這種情況下,替代第一次出現$1:(注意:)例如。 apple:;$1":"也可以寫爲$1FS,其中FS is :

如果你有幾百萬行的在你的文件,則該方法將是最快的,因爲它不涉及任何預處理,也是我們沒有使用任何其他數據結構數組說用於存儲您的密鑰或記錄。

+0

感謝您的好解釋。 – MichaelD

+0

@邁克爾D:歡迎邁克爾。 – batMan

相關問題