awk |添加新行或更新文件中的现有行

时间:2013-12-08 14:57:07

标签: awk

我想在 file2 的基础上更新 file1 。如果file2中的任何行是新的,那么它应该添加到file1中。如果file2中的任何行已经在file1中,那么如果file2中的时间更长,则用file2中的行更新该行。

文件1

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  

注意:

  • 第二个字段在输出中应该是唯一的。
  • 新值的添加:file2中的最新第二字段值“1111111104”是基于日期列(第3字段)的较新(201312051016)然后旧值(201312051014)。
  • 更新现有值:根据第3栏中的日期更新“1111111102”及更新值
  • file1非常大,而file2只有5-10个条目。
  • 第二个字段“1111111101”的行不需要更新,因为它file1中的条目已经与file2中的新日期“201312041013”相比具有最新日期“201312051014”。 / LI>

我对此没有太多尝试,因为作为初学者,它确实具有复杂的条件..

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];  
}  

2 个答案:

答案 0 :(得分: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的假设,因为它仅对file2中的少数条目使用数组。
  • 新条目只是附加到输出中。没有排序。如果需要,则需要额外的努力。

修改

Heeding @ 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

后者当然适用于两种版本的脚本。

答案 1 :(得分: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保留在内存中,并且只在处理完整个输入时打印任何内容。毋庸置疑,这比目前的设计使用了更多的内存,并且因此通常效率较低。不过,如果需要,可以做到。