2013-12-08 38 views
1

我想更新文件1基于文件2。如果file2中有任何新行,则应将其添加到file1中。如果file2中的任何一行已经存在于file1中,那么如果file2中的时间更长,那么使用file2中的行更新该行。awk |添加新行或更新文件中的现有行

file1的

DL,1111111100,201312051013,val,FIX01,OptIn,N,Ext1,Ext2 
DL,1111111101,201312051014,val,FIX01,OptIn,Y,Ext1,Ext2 
DL,1111111102,201312051015,val,FIX01,OptIn,Y,Ext1,Ext2 
DL,1111111103,201312051016,val,FIX01,OptIn,N,Ext1,Ext2 

file2的

DL,1111111101,201312041013,val,FIX02,OptIn,N,Ext1,Ext2 
DL,1111111102,201312051016,val,FIX02,OptIn,N,Ext1,Ext2 
DL,1111111102,201312051017,val,FIX02,OptIn,N,Ext1,Ext2 
DL,1111111104,201312051014,val,FIX01,OptIn,Y,Ext1,Ext2 
DL,1111111104,201312051016,val,FIX02,OptIn,Y,Ext1,Ext2 

newfile1

DL,1111111100,201312051013,val,FIX01,OptIn,N,Ext1,Ext2 
DL,1111111101,201312051014,val,FIX01,OptIn,Y,Ext1,Ext2 
DL,1111111102,201312051017,val,FIX02,OptIn,N,Ext1,Ext2 
DL,1111111103,201312051016,val,FIX01,OptIn,N,Ext1,Ext2 
DL,1111111104,201312051016,val,FIX02,OptIn,Y,Ext1,Ext2 

注:

  • 第二场建议立即进行删除d在输出中是唯一的。
  • 添加新值:根据日期列(第3字段)取file2中值为“1111111104”的最新第2字段,其为更新(201312051016),然后是旧值(201312051014)。
  • 更新现有值:根据第3列中的日期更新“1111111102”
  • file1非常大,而file2只有5-10个条目。
  • 行与第二个字段“1111111101”不需要更新,因为它在file1中的条目已经具有最新日期“201312051014”与在file2中的新日期“201312041013”相比较。

我没有尝试过很多关于这个,因为它真的有,我作为初学者复杂条件..

BEGIN { FS = OFS = "," } 
FNR == NR { 
    m=$2; 
    a[m] = $0; 
    next 
} 
{ 
    if($2 in a) 
    { 
     split(a[$2],datetime,",") 
     if($3>datetime[3]) 
       print $0; 
     else 
       print a[$2]"Old time" 
    } 
    else print $0"NOMATCH"; 
    delete a[$2]; 
} 
+0

什么问题?我不知道你在努力达到什么目的。尝试解释你想要的而不是仅仅发布一些文件和无法理解的笔记 - 然后有人会提供帮助。 –

+0

我对此表示歉意,如果我无法正确解释它。现在我已经添加了小的摘要。让我知道如果我需要解释mote。 –

回答

2

由于file1是非常大的,但file2很小(5-10项)您需要首先将所有file2读取到内存中,处理重复值。因此,您将拥有一个由新数据的记录编号索引的数组;你还应该在单独的数组中记录每条记录的日期。然后,在读取主文件时,查看阵列中的记录号和日期,如果需要,可以将保存的新记录替换为传入的旧记录。

你的大纲脚本是大多数的方式。因为你没有保存日期未来在更复杂的这或多或少作品:

awk -F, ' 
FNR == NR { if (!($2 in date) || date[$2] < $3) { date[$2] = $3; line[$2] = $0; } next; } 
      { if ($2 in date) 
      { 
       if (date[$2] > $3) 
        print line[$2] 
       else 
        print 
       delete line[$2] 
       delete date[$2] 
      } 
      else 
       print 
      } 
END  { for (l in line) print line[l]; }' file2 file1 

样本输出给定的数据:

DL,1111111100,201312051013,val,FIX01,OptIn,N,Ext1,Ext2 
DL,1111111101,201312051014,val,FIX01,OptIn,Y,Ext1,Ext2 
DL,1111111102,201312051017,val,FIX02,OptIn,N,Ext1,Ext2 
DL,1111111103,201312051016,val,FIX01,OptIn,N,Ext1,Ext2 
DL,1111111104,201312051016,val,FIX02,OptIn,Y,Ext1,Ext2 

但是,如果有4条新的记录,不能保证他们会按照排序顺序排列,尽管他们都将排在最后。如果输入保证按排序顺序,则可以升级脚本以在列表的适当位置打印新记录。您只需搜索行的列表以查看是否在当前行之前应该打印任何行,如果是,请执行此操作(并删除记录以使它们不会在最后打印)。

请注意,输出的唯一性取决于输入的唯一性(file1)。也就是说,如果输入中的字段2重复,则此代码不会注意。即使发现重复,目前的设计也无法完成;旧行已被打印,因此打印新行会导致重复。如果您担心这一点,您可以设计awk脚本以将整个file1保留在内存中,并且只在处理完整个输入时才打印任何内容。不用说,这比现在的设计使用了更多的内存,因此通常效率会更低。尽管如此,如果需要的话也可以完成。

+0

谢谢乔纳森..这很快你很容易..也可以请你解释第二条件.. ..?第二个“if”是以“{”..开始的,这是在第一个条件之后的文件上的第二个循环吗? –

+0

有三个街区。第一个是'FNR == NR'块;它处理的第一个文件处理又名'file2'(因为这是当文件记录号匹配整体记录号,并且因为'next'跳过给定行的其余处理)。下一个块是你指的那个(我认为);它适用于第二个文件中的每一行。它会查看记录号是否已知,如果是,并且替换日期比文件('file1')中的日期更新,则打印修订;无论如何,它将清除记录。 –

+0

如果记录不在'file2'数据中,则记录被简单地打印。第三个块是“END”块。它会查找仍然留在'line'数组中的任何记录并将其打印出来;它们没有与'file1'中的任何内容匹配,所以它们必须被添加。 –

3

假设你可以按如下启动awk

awk -f script.awk input2.csv input1.csv > result.csv 

您可以使用下面的脚本来获得所需的输出:

BEGIN { 
    FS = OFS = "," 
} 
FILENAME == "input2.csv" { 
    date[$2] = $3 
    data[$2] = $0 
    used[$2] = 0 
} 
FILENAME == "input1.csv" { 
    if ($2 in date) { 
     used[$2] = 1 
     if ($3 < date[$2]) 
      print data[$2] 
     else 
      print $0 
    } else { 
     print $0 
    } 
} 
END { 
    for (key in used) { 
     if (used[key] == 0) 
      print data[key] 
    } 
} 

注:

  • 脚本利用了file2小于file1的假设,因为它使用了一个数组onl y表示file2中的少数条目。
  • 新条目只是附加到输出。没有排序。如果这是必需的,那就需要额外的努力。

编辑

听取@ JonathanLeffler的关于我确定哪些文件被处理,我想提供一个替代版本,可能(或可能不会:-))是一个更直接一点的方式句话转发了解比检查NR=FNR。然而,它仅适用于的awk足够新的版本,它们能够返回一个数组的大小为length(array)

BEGIN { 
    FS = "," 
} 
{ 
    # The following effectively creates an array entry for each filename found (for "known" filenames existing entries are overwritten). 
    files[FILENAME] = 1 
    # check the number of files we have so far 
    if (length(files) == 1) { 
     # we are still in the first file 
     date[$2] = $3 
     data[$2] = $0 
     used[$2] = 0 
    } else { 
     # we are in the second file (or any other following file) 
     if ($2 in date) { 
      used[$2] = 1 
      if ($3 < date[$2]) 
       print data[$2] 
      else 
       print $0 
     } else { 
      print $0 
     } 
    } 
} 
END { 
    for (key in used) { 
     if (used[key] == 0) 
      print data[key] 
    } 
} 

另外,如果您需要您的输出,以根据第二排可以更换进行排序这个调用awk

awk -f script.awk input2.csv input1.csv | sort -t "," -n -k 2 > result.csv 

后者,当然,工程脚本的两个版本。

+0

基本逻辑看起来很合理,而且与我使用的非常相似(我从数组中删除了使用的条目;您使用的auxilliary数组用于传达相同的信息)。但是,通过键入这两个文件名,可以使代码非常脆弱,而我的代码可以使用'input2.csv'和'input1.csv'以及'file2'和'file1'。在某些方面,'FNR == NR'成语不太令人满意,但对于处理第一个文件与其他文件不同,它确实很好。 –

+0

谢谢马库斯..你的解决方案和乔纳森一样工作得很好.. :)你摇滚 –