匹配文件之間的 2 個主要列;並在這些主列匹配時將其他列粘貼到輸出文件中。保持第一個文件的行大小不變
這是我大約 24 小時前提出的上一個問題的後續問題:在文件之間同時匹配兩個主要列,並在這些主要列匹配時將補充列粘貼到輸出文件中
G-Man 用一個有用的程式碼解決了這個問題,但我有一個後續問題。我已經接受了答案,因此第二篇文章……
我有 3 個文件,每個文件都有唯一數量的列,全部以製表符分隔,但有些列在 3 個文件之間共享。這是我想用來創建某種“聚合”文件的 3 個文件之間的共享列。
下表顯示了文件的外觀範例。基本上我想在文件之間匹配列 MAIN1 和 MAIN2 。三個文件之間的兩列都必須匹配。
當兩個文件之間的 MAIN1 和 MAIN2 匹配時,我想將 file2 中的列“minor8”添加到 file1 中表的右側。隨後,對於兩個文件之間的 MAIN1 和 MAIN2 匹配的情況,我想在 file1 表的右側從 file3 添加“minor9”。因為“minor8”應該緊挨著file1的最右邊的列(列名:“minor3”),所以我希望“minor9”在“minor8”旁邊進入新的輸出文件。OUTPUT 文件給出了我理想的最終文件應該是什麼樣子的想法。
基本上這些是 3 個文件的範例(“選項卡”有點混亂)
文件1:
MAIN1 minor1 MAIN2 minor3 1 bla1 a blabla1 1 bla2 b blabla2 1 bla3 c blabla3 2 bla4 a blabla4 2 bla5 d blabla5 3 bla6 e blabla6 4 bla7 f blabla7 5 bla8 a blabla8 5 bla9 g blabla9
文件2:
minor8 MAIN1 MAIN2 yes1 2 d yes2 3 e yes3 4 f yes4 5 a yes5 5 g yes6 1 a yes7 1 b yes8 1 c yes9 2 a
文件3:
MAIN1 MAIN2 minor9 5 a sure1 5 g sure2 1 a sure3 1 b sure4 1 c sure5 2 a sure6 2 d sure7 3 e sure8 4 f sure9
所需的輸出文件:
MAIN1 minor1 MAIN2 minor3 minor8 minor9 1 bla1 a blabla1 yes6 sure3 1 bla2 b blabla2 yes7 sure4 1 bla3 c blabla3 yes8 sure5 2 bla4 a blabla4 yes9 sure6 2 bla5 d blabla5 yes1 sure7 3 bla6 e blabla6 yes2 sure8 4 bla7 f blabla7 yes3 sure9 5 bla8 a blabla8 yes4 sure1 5 bla9 g blabla9 yes5 sure2
如前所述,G-Man 提供了一個有用的程式碼,完全符合我的要求(請參閱上一篇文章)。我可能會問 G-Man(或其他有時間的人)一些關於我還不太了解的個別程式碼行的具體問題,但在那之前,我有這個後續問題。
G-Man 的程式碼能夠重新創建上述 OUTPUT 文件,所以謝謝 G-Man!
後續問題:
我忘記提到的一件事是,程式碼無法做到(據我所知),如果文件之間的列 MAIN1 和 MAIN2 不匹配,它將從 file1 中刪除行。這是我的錯,因為我沒有具體說明。我的目標是有一個 OUTPUT 文件,其中沒有刪除 file1 中的任何行。
基本上 file1 是我的優先文件。無論這個文件有多少行(接近一百萬),這也是 OUTPUT 文件應該有的行數。如果沒有列 MAIN1、MAIN2 匹配,則某些行的列“minor8”和“minor9”可以為空。但是,當“minor8”或“minor9”(或兩者)存在“缺失/空”值時,我想保留這些行 file1。
我將嘗試使用上述文件 2 和 3 的略微不同版本來說明這一點(因此 file1 保持不變)。
調整後的 file2(沒有 MAIN1、MAIN2 組合:2、d):
minor8 MAIN1 MAIN2 yes2 3 e yes3 4 f yes4 5 a yes5 5 g yes6 1 a yes7 1 b yes8 1 c yes9 2 a
調整後的file3(沒有MAIN1,MAIN2組合:5,a):
MAIN1 MAIN2 minor9 5 g sure2 1 a sure3 1 b sure4 1 c sure5 2 a sure6 2 d sure7 3 e sure8 4 f sure9
調整後的所需輸出(即,對於 MAIN1、MAIN2 組合 2-d,minor8 列中的空值;對於 MAIN1、MAIN2 組合 5-a,minor9 列中的空值):
MAIN1 minor1 MAIN2 minor3 minor8 minor9 1 bla1 a blabla1 yes6 sure3 1 bla2 b blabla2 yes7 sure4 1 bla3 c blabla3 yes8 sure5 2 bla4 a blabla4 yes9 sure6 2 bla5 d blabla5 sure7 3 bla6 e blabla6 yes2 sure8 4 bla7 f blabla7 yes3 sure9 5 bla8 a blabla8 yes4 5 bla9 g blabla9 yes5 sure2
我希望我的解釋方式足夠清楚。我看到表格的選項卡有點亂。你們喜歡這樣,還是讓我在視覺上理順表格?(我可以想像,唯一可能導致的問題是,當您複製粘貼我的範例數據時,您將擁有不應該存在的其他選項卡……)
無論如何,我非常感謝你們的幫助。希望在不久的將來,我能夠為這個論壇做出貢獻,而不僅僅是尋求幫助……
您對如何編輯 G-Man 的程式碼以使其成為可能有什麼建議嗎?或者,如果您有一個完全不同的建議,如何編寫一個考慮到這一額外要求的有用程式碼,請告訴我。
創建以下文件:
merge21
:開始 { FS = "\t" OFS = "\t" } NR==FNR { # **file2** key = **$2 "," $3** 存在[鍵] = 1 **次要8** [鍵] = **1** 下一個 } { #**文件1** 鍵 = $1 "," $3 if (present[key]) 列印 $1, $2, $3, $4, **minor8[key]** 否則列印 $1, $2, $3, $4, "-" }
merge312
:開始 { FS = "\t" OFS = "\t" } NR==FNR { # **file3** key = **$1 "," $2** 存在[鍵] = 1 **次要 9** [鍵] = **3 美元** 下一個 } { #**文件 1 + 文件 2** 鍵 = $1 "," $3 if (present[key]) 列印 $1, $2, $3, $4, **$5, minor9[key]** 否則列印 $1, $2, $3, $4, **$5** , "-" }
它們幾乎相同;我已經加粗了差異。現在輸入命令
awk -f merge21 file2 file1 | awk -f merge312 file3 -
這假定您的關鍵欄位都不包含逗號,並且您的數據都不包含連字元,但這實際上僅取決於數據中沒有出現一些字元串。擴展它以支持更多列將是微不足道的;我希望這是顯而易見的。這可以增強以在一次
awk
執行中完成所有操作,但這會有點複雜,並且(IMNSHO)不值得付出努力。這會產生文件中數據的所謂“左外連接”;有關某些定義,請參閱Stack Overflow 上的INNER 和 OUTER 聯接之間的區別。(“左外連接”在該問題的公認答案中定義為(釋義)«第一個表中的所有行,加上其他表中的任何公共行»。)
你的輸出將是
MAIN1 minor1 MAIN2 minor3 minor8 minor9 1 bla1 a blabla1 yes6 sure3 1 bla2 b blabla2 yes7 sure4 1 bla3 c blabla3 yes8 sure5 2 bla4 a blabla4 yes9 sure6 2 bla5 d blabla5 - sure7 3 bla6 e blabla6 yes2 sure8 4 bla7 f blabla7 yes3 sure9 5 bla8 a blabla8 yes4 - 5 bla9 g blabla9 yes5 sure2
而且,顯然,您可以使用 . 刪除**
-
**字元sed
。(當然,如果您的真實數據實際上包含連字元,請選擇一些未使用的字元或字元串作為缺失數據的佔位符。)筆記
FS
和OFS
分別是輸入欄位分隔符和輸出欄位分隔符。(顯然IFS
在 中沒有意義awk
;這是我的錯誤。)您可能並不真正需要FS="\t"
-awk
預設情況下將製表符辨識為輸入欄位分隔符。(它允許您擁有包含空格的欄位,但您似乎對此不感興趣。)OFS="\t"
很重要;正因為如此,我可以說print $1, $2, $3, $4
並讓輸入欄位與它們之間的選項卡一起輸出。如果我不說OFS="\t"
,它們將被空格分隔,除非我說print $1 "\t" $2 "\t" $3 "\t" $4
,這很乏味並且有損可讀性。- 如果您對 MAIN1 和 MAIN2 給出了額外的限制——例如,它們總是每個只有一個字元,或者 MAIN1 總是一個數字,而 MAIN2 總是以字母開頭——我就不需要
,
在key
. 但是您的第一個問題的原始版本沒有顯示出這樣的限制。考慮以下數據:MAIN1 ($2) MAIN2 ($3) badkey = $2 $3 goodkey = $2 "," $3 2 34151 234151 2,34151 23 4151 234151 23,4151
如果我們不在鍵中包含一些不會出現在鍵欄位(MAIN1 和 MAIN2)中的分隔符,我們可以
key
為不同的行獲得相同的值。
- 冒著分叉的風險,我不會“告訴 Linux”任何事情。我在告訴我
awk
該怎麼做。- 關於程式碼
NR==FNR { # 文件 3 鍵 = $1 "," $2 存在[鍵] = 1 次要9 [鍵] = $ 3 下一個 }
考慮 的倒數第七行
file3
,其中包含1 a sure3
。顯然我們有$1
=1
、$2
=a
和$3
=sure3
,所以key
=1,a
。present[key] = 1
表示我正在設置present["1,a"]
為1
標誌以指示file3
有一條1,a
線;即,=有一個minor9
值。由於沒有行 in ,沒有被設置,所以程式碼的 " " 部分知道沒有for = ,它應該列印出來。這個名字只是我的一個任意選擇。它表示該行存在於key``1,a``5,a``file3``present["5,a"]``file1 + file2``minor9``key``5,a
-
present``1,a``file3
(並且該5,a
行不是)。習慣上用它1
來表示“TRUE”。
- 您可以替換
print $1, $2, $3, $4
為for (n=1; n<=4; n++) printf "%s\t", $n
. 您應該通過對最後一個欄位使用普通printf
)或通過執行來結束該行printf "\n"
。您可以通過執行類似的操作來進一步簡化for (n=1; n<=4; n++) printf "%s\t", $n if (present[key]) print minor8[key] 否則列印“-”
請閱讀awk(1)、POSIX 規範
awk
、 GNU Awk 使用者指南,並查看Awk.info了解更多資訊。