类似Vlookup的函数在ksh中使用awk

时间:2016-09-24 15:27:08

标签: arrays awk ksh vlookup aix

免责声明:

1)英语是我的第二语言,所以请原谅你可能发现的任何语法恐怖。我非常有信心,尽管如此,你仍然能够理解我的需要。

2)我在本网站找到了几个与我的问题/问题类似的例子,但遗憾的是我无法弄清楚需要引入的修改以满足我的需求。

3)你会在这里和那里找到一些大写字母的文字。当然不是我"大喊大叫"在你身边,但只有一种方法可以使部分文字脱颖而出。 Plase不认为这是一种不礼貌的行为。

4)对于那些活到这本中篇小说的人而言,感谢你们的耐心,即使你们没有能够/感觉像是帮助我。我在这里的发言人就是这样一个事实:在浏览网站一段时间后,我注意到最常见的抱怨"来自愿意帮助的人似乎缺乏寻求帮助的人提供的信息(和/或缺乏质量)。然后,如果需要的话,我宁愿被指控为赎罪......这至少不是一种普通的罪行......

"问题":

我有2个文件(a和b用于简化)。文件a有7列,以逗号分隔。文件b有两列用逗号分隔。

我需要的是:每当文件a的第7列中的数据匹配-EXACT MATCHES ONLY-文件b的第1列上的数据时,新行,包含文件的整行a和文件b的第2列将附加到新文件" c"。

---在底部的笔记中更多信息---

提交a:

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)注意如何与ID" aaaaaaaa"在文件中a不会使它成为文件c,因为ID" aaaaaaaa"文件b中不存在。同样,带有ID" bbbbbbbb"在文件b中没有使它成为文件c,因为ID" bbbbbbbb"在文件a中不存在,因此它首先不会被注意。

1)数据显然是由于机密性问题完全制作出来的,尽管提供的示例与真实文件的外观非常相似。

2)我添加标题只是为了更好地了解数据的性质。真实文件没有,所以不需要在源文件上跳过它们,也不需要在目标文件中创建它。

3)两个文件默认排序,这意味着ID将在文件b中正确排序,而它们很可能在文件a中被加扰。文件c应该最好遵循文件a的顺序(虽然我可以稍后操作以满足我的需要,所以不用担心,只要代码执行我需要的东西并且不会通过组合数据来搞乱数据错线)。

4)非常非常重要:

4.a)我已经有一个"工作"使用" cat"," grep","同时" ksh代码(附后)和"如果"做这个工作。它就像一个魅力(好吧,可以接受)有160K线的样本文件(它能够输出60K行 - 一小时 - 这在预测中会产生可接受的" 20天"生产3000万行[保持阅读]),但不知何故(我有足够的处理器和内存容量)猫和/或grep似乎正在努力处理现实生活中的5万亿行文件(文件a和b都可以有每个3000万行,这样得到的文件中最大可能的行数,即使假设文件中的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" section不仅仅是用来表达输出行的一种方法,而是服务于双重目的,同时安全防范任何可能来自grep,IE 100匹配1000的误报(请记住,正如我之前提到的,我我在AIX上工作,所以我的grep没有GNU的-m开关,我需要匹配精确/绝对)。

你已经到了尽头。恭喜!你已经获得了耐心的奖章。

2 个答案:

答案 0 :(得分:3)

$ 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

答案 1 :(得分:1)

编辑:更新了计算 您可以尝试预测您拨打其他程序的频率:
对于文件b中的每一行,每行至少7 awk&s + 1 cat + 1 grep a乘以2 awk' s。 (9 * 160.000)。
对于文件b:2 awk&s,一个文件打开,每个匹配一个文件关闭。输出60K,即4 * 60.000。

代码中的一个小变化可以将其改为"仅#34; 160.000倍grep:

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文件,看看速度有多快 在我解释这仍然是错误的方法之前,我将进行另一个小的改进:我将把cat移动到while循环(在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,如What is "NR==FNR" in awk?中所述,并在@jas的答案中找到。 它只有一个系统调用,两个文件只读一次。