簡體   English   中英

在Ksh中使用Awk的類似於Vlookup的函數

[英]Vlookup-like function using awk in ksh

免責聲明:

1)英語是我的第二語言,因此請原諒您可能會發現的任何語法恐怖。 我非常有信心,盡管有這些,您仍然能夠理解我的需求。

2)我很遺憾地在這個站點上找到了一些示例,這些示例解決了與我的問題類似的問題,盡管不幸的是,我無法弄清楚需要進行哪些修改才能滿足我的需求。

3)您會在此找到大寫字母的文字。 當然不是我對您“喊”,而只是使文本部分突出的一種方法。 Plase認為這不是不禮貌的行為。

4)對於那些活着活着中篇小說的人,即使您無法獲得幫助/幫助我,也要感謝您的耐心等待。 在此我感到不滿的事實是,在網站上瀏覽了一段時間之后,我注意到願意提供幫助的人們最常見的“投訴”似乎是他們提供的信息不足(和/或缺乏質量)。尋求幫助。 然后,如果需要的話,我寧願被指控改寫……至少這不是常見的冒犯行為……


問題”:

我有2個文件(為簡化起見,a和b)。 文件a有7列,以逗號分隔。 文件b有兩列,用逗號分隔。

我需要什么:只要文件a的第7列中的數據匹配-僅精確匹配-文件b的第1列上的數據,就要換行,包含文件b的整行以及文件b的第2列附加到新文件“ c”中。

---底部注釋中的更多信息---

提交一個:

Server Name,File System,Path,File,Date,Type,ID
horror,/tmp,foldera/folder/b/folderc,binaryfile.bin,2014-01-21 22:21:59.000000,typet,aaaaaaaa
host1,/,somefolder,test1.txt,2016-08-18 00:00:20.000000,typez,11111111
host20,/,somefolder/somesubfolder,usr.cfg,2015-12-288 05:00:20.000000,typen,22222222
hoster,/lol,foolie,anotherfile.sad,2014-01-21 22:21:59.000000,typelol,66666666
hostie,/,someotherfolder,somefile.txt,2016-06-17 18:43:12.000000,typea,33333333
hostile,/sad,folder22,higefile.hug,2016-06-17 18:43:12.000000,typeasd,77777777
hostin,/var,folder30,someotherfile.cfg,2014-01-21 22:21:59.000000,typo,44444444
hostn,/usr,foldie,tinyfile.lol,2016-08-18 00:00:20.000000,typewhatever,55555555
server10,/usr,foldern,tempfile.tmp,2016-06-17 18:43:12.000000,tipesad,99999999

文件b:

ID,Size
11111111,215915
22222222,1716
33333333,212856
44444444,1729
55555555,215927
66666666,1728
88888888,1729
99999999,213876
bbbbbbbb,26669080

預期文件c:

Server Name,File System,Path,File,Date,Type,ID,Size
host1,/,somefolder,test1.txt,2016-08-18 00:00:20.000000,typez,11111111,215915
host20,/,somefolder/somesubfolder,usr.cfg,2015-12-288 05:00:20.000000,typen,22222222,1716
hoster,/lol,foolie,anotherfile.sad,2014-01-21 22:21:59.000000,typelol,66666666,1728
hostie,/,someotherfolder,somefile.txt,2016-06-17 18:43:12.000000,typea,33333333,212856
hostin,/var,folder30,someotherfile.cfg,2014-01-21 22:21:59.000000,typo,44444444,1729
hostn,/usr,foldie,tinyfile.lol,2016-08-18 00:00:20.000000,typewhatever,55555555,215927
server10,/usr,foldern,tempfile.tmp,2016-06-17 18:43:12.000000,tipesad,99999999,213876

補充筆記:

0)注意文件a中ID為“ aaaaaaaa”的行如何不進入文件c,因為文件b中不存在ID“ aaaaaaaa”。 同樣,文件b中ID為“ bbbbbbbb”的行也不會進入文件c,因為文件a中不存在ID“ bbbbbbbb”,因此從一開始就不會尋找它。

1)由於保密性問題,雖然顯然提供的示例與真實文件的外觀十分相似,但數據顯然已完全制成。

2)我添加了標頭只是為了更好地了解數據的性質。 實際文件沒有此文件,因此無需在源文件上跳過它們或在目標文件中創建它們。

3)默認情況下,兩個文件都進行排序,這意味着ID將在文件b中正確排序,而ID很有可能在文件a中被加密。 文件c應該最好遵循文件a的順序(盡管以后我仍然可以操作以滿足我的需要,所以在那里不用擔心,只要代碼滿足我的需要並且不會通過組合錯誤的行而使數據混亂)。

4)非常非常重要:

4.a)我已經有一個“有效的” ksh代碼(如下所示),它使用“ cat”,“ grep”,“ while”和“ if”來完成工作。 它具有16萬行樣本文件的魅力(很好,可以接受)(它可以輸出大約60K行-大約一個小時,按計划,這將產生一個可接受的“ 20天”來生產3000萬行[KEEP閱讀中]),但是不知何故(我有足夠的處理器和內存容量),cat和/或grep似乎在處理現實生活中的500萬行文件(文件a和b最多可以有3000萬行,因此,這就是結果文件中最大的行數,即使假設文件a中的100%行與文件b)中的行匹配,現在c文件每24小時僅饋送幾百行。

4.b)有人告訴我,awk(更強大)應該在我使用的較弱的命令似乎失敗的情況下成功。 我還被告知,使用數組可能是解決我的性能問題的方法,因為所有數據都立即升華到內存中並從那里開始工作,而不必費力。 正如我目前所做的那樣,grep文件b的次數與文件a中的行的次數相同。

4.c)我在AIX上工作,所以我只有sh和ksh,沒有bash,因此我不能使用后者提供的數組工具,這就是為什么我想到AWK的原因,而且我認為AWK可能是“更強”,盡管我可能是錯誤的。

現在,我向您展示一段宏偉的ksh代碼(這里很明顯地諷刺,盡管我喜歡您在腦海中短暫描繪一下猴子舉起並向所有其他叢林爬行者展示他們未來的獅子王的形象的想法。 )我已經設法發展(在閱讀此代碼時,您可以根據需要隨意笑,無論如何我都無法聽到您的聲音,因此不會傷害您的:P):

cat "${file_a}" | while read -r line_file_a; do

    server_name_file_a=`echo "${line_file_a}" | awk -F"," '{print $1}'`
    filespace_name_file_a=`echo "${line_file_a}" | awk -F"," '{print $2}'`
    folder_name_file_a=`echo "${line_file_a}" | awk -F"," '{print $3}'`
    file_name_file_a=`echo "${line_file_a}" | awk -F"," '{print $4}'`
    file_date_file_a=`echo "${line_file_a}" | awk -F"," '{print $5}'`
    file_type_file_a=`echo "${line_file_a}" | awk -F"," '{print $6}'`
    file_id_file_a=`echo "${line_file_a}" | awk -F"," '{print $7}'`

    cat "${file_b}" | grep ${object_id_file_a} | while read -r line_file_b; do

        file_id_file_b=`echo "${line_file_b}" | awk -F"," '{print $1}'`
        file_size_file_b=`echo "${line_file_b}" | awk -F"," '{print $2}'`

        if [ "${file_id_file_a}" = "${file_id_file_b}" ]; then

            echo "${server_name_file_a},${filespace_name_file_a},${folder_name_file_a},${file_name_file_a},${file_date_file_a},${file_type_file_a},${file_id_file_a},${file_size_file_b}" >> ${file_c}.csv

        fi

    done

done

最后一個補充說明,以防萬一您想知道:

“ if”部分不僅用作表達輸出線的手段,而且還具有雙重目的,同時安全地驗證可能源自grep(IE 100匹配1000)的任何誤報(請注意,前面提到過,我正在AIX上工作,因此我的grep沒有使用GNU所具有的-m開關,因此我需要匹配(精確/絕對)。

您已經到了盡頭。 恭喜! 您已獲得耐心獎牌。

$ cat stuff.awk
BEGIN { FS=OFS="," }
NR == FNR { a[$1] = $2; next }
$7 in a { print $0, a[$7] }

請注意將文件提供給awk命令的順序,首先是b ,然后是a

$ awk -f stuff.awk b.txt a.txt
host1,/,somefolder,test1.txt,2016-08-18 00:00:20.000000,typez,11111111,215915
host20,/,somefolder/somesubfolder,usr.cfg,2015-12-288 05:00:20.000000,typen,22222222,1716
hoster,/lol,foolie,anotherfile.sad,2014-01-21 22:21:59.000000,typelol,66666666,1728
hostie,/,someotherfolder,somefile.txt,2016-06-17 18:43:12.000000,typea,33333333,212856
hostin,/var,folder30,someotherfile.cfg,2014-01-21 22:21:59.000000,typo,44444444,1729
hostn,/usr,foldie,tinyfile.lol,2016-08-18 00:00:20.000000,typewhatever,55555555,215927
server10,/usr,foldern,tempfile.tmp,2016-06-17 18:43:12.000000,tipesad,99999999,213876

編輯:更新的計算您可以嘗試預測您調用另一個程序的頻率:
文件a中每行至少7 awk +1 cat + 1 grep乘以文件b中每行2 awk。 (9 * 160.000)。
對於文件b:2個awk,每次命中一個文件打開,一個文件關閉。 如果輸出為60K,則為4 * 60.000。

對代碼進行少量更改即可將其更改為grep的“僅” 160.000倍:

cat "${file_a}" | while IFS=, read -r server_name_file_a \
   filespace_name_file_a folder_name_file_a file_name_file_a \
   file_date_file_a file_type_file_a file_id_file_a; do
   grep "${object_id_file_a}" "${file_b}" | while IFS="," read -r line_file_b; do
        if [ "${file_id_file_a}" = "${file_id_file_b}" ]; then
            echo "${server_name_file_a},${filespace_name_file_a},${folder_name_file_a},${file_name_file_a},${file_date_file_a},${file_type_file_a},${file_id_file_a},${file_size_file_b}" 
        fi
    done
done >> ${file_c}.csv

好吧,嘗試使用您的160K文件,看看它有多快。
在我解釋這仍然是錯誤的方法之前,我將進行另一個小的改進:我將while循環中的cat移至末尾( done后)。

while IFS=, read -r server_name_file_a \
   filespace_name_file_a folder_name_file_a file_name_file_a \
   file_date_file_a file_type_file_a file_id_file_a; do
   grep "${object_id_file_a}" "${file_b}" | while IFS="," read -r line_file_b; do
        if [ "${file_id_file_a}" = "${file_id_file_b}" ]; then
            echo "${server_name_file_a},${filespace_name_file_a},${folder_name_file_a},${file_name_file_a},${file_date_file_a},${file_type_file_a},${file_id_file_a},${file_size_file_b}" 
        fi
    done
done < "${file_a}" >> ${file_c}.csv

該解決方案的主要缺點是,您將使用grep反復讀取文件a中每一行的完整file_b。

這個解決方案在性能上是一個不錯的改進,但是grep仍然有很多開銷。 使用awk可以發現另一個巨大的改進。
最佳解決方案是使用awk,如awk中的“ NR == FNR”是什么? 並在@jas的答案中找到。 這只是一個系統調用,並且兩個文件都只能讀取一次。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM