2013-12-08 63 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

謝謝馬庫斯..你的解決方案和喬納森一樣工作得很好.. :)你搖滾 –