2016-08-30 30 views
0

我將不勝感激這個腳本任務的任何幫助。bash,awk,sed刪除帶有重複ID的XML塊,保持最新狀態,保持原始順序

我需要刪除每個塊的非唯一ID,除了有一個最新的日期。如果日期相同,則文件內的最後一項應該取勝並保持未刪除。

必須保留輸入的原始排序順序。

輸入:

<DATA> 
<TABLES> 

<BLOCK> 
<ID V="333"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<TEXT/> 
<ID V="4444"/> 
<DATE V="20140101 00:00:00"/> 
<TEXT/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<ID V="333"/> 
<DATE V="20100101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="4444"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="7777777"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<ID V="333"/> 
<DATE V="20120101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<TEXT/> 
<ID V="22"/> 
<TEXT/> 
<DATE V="20151231 00:00:00"/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="7777777"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="22"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

</TABLES> 
</DATA> 

預期輸出:

<DATA> 
<TABLES> 

<BLOCK> 
<ID V="333"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="4444"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<TEXT/> 
<ID V="22"/> 
<TEXT/> 
<DATE V="20151231 00:00:00"/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="7777777"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

</TABLES> 
</DATA> 
+1

聽起來像是你需要一個實際的XML解析器,而不是一些黑客使用awk和sed。 –

+0

「請寫一個腳本,爲我做X」不是一個合適的StackOverflow問題;我們在這裏建立一個技術問答知識庫,而不是爲他們編寫人們的腳本。 (另外,bash不是這份工作的精選語言 - 你可以更容易地使用Python)。 –

+0

(順便說一句,根據這個標準選擇要刪除的項目對於XPath 2.0來說是一件很容易的事情;儘管如此,這與基於libxml的命令行工具(如xmlstarlet,xsltproc和c。支持) 。 –

回答

1

從你的問題來看,在你的問題的評論中提到的輸出順序並不完全清楚,但這是一種解釋 - 它將按輸入文件中出現的順序循環顯示記錄並打印每個記錄僅當它是文件中包含id的最大日期的最後一個時。它可以在任何UNIX系統上運行awk。

$ cat tst.awk 
BEGIN { RS=""; ORS="\n\n" } 
{ 
    id = date = $0 
    gsub(/.*\n<ID V="|".*/,"",id) 
    gsub(/.*\n<DATE V="|".*/,"",date) 
} 

date >= id2maxDate[id] { 
    delete maxDateRecNr2rec[id2maxDateRecNr[id]] 
    id2maxDateRecNr[id] = NR 
    maxDateRecNr2rec[NR] = $0 
    id2maxDate[id]  = date 
} 

END { 
    for (recNr=1; recNr<=NR; recNr++) { 
     if (recNr in maxDateRecNr2rec) { 
      print maxDateRecNr2rec[recNr] 
     } 
    } 
} 

$ awk -f tst.awk file 
<BLOCK> 
<TEXT/> 
<ID V="4444"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<ID V="333"/> 
<DATE V="20120101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<TEXT/> 
<ID V="22"/> 
<TEXT/> 
<DATE V="20151231 00:00:00"/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="7777777"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

你說你的問題date但我假設由你真正的意思是無論是在你輸入的DATE領域如此,因爲所有的時間是午夜不要緊爲您發佈的例子,但上面使用日期+時間,即DATE字段的全部內容。如果你想一天的時間從計算中排除,然後只需要改變:

gsub(/.*\n<DATE V="|".*/,"",date) 

gsub(/.*\n<DATE V="| .*/,"",date) 
0

與AWK 3.1.8測試AWK溶液 - 輸出用於每個ID最新記錄 - 保留在輸入的記錄相對順序

awk -F'\n' ' 
BEGIN{           
    ORS=RS="\n</BLOCK>"       
} 
{ 
    id=date=""          
    for(i=1; i <=NF; ++i) {      
    if(id && date) break 
    if($i ~ /<ID V=\"[0-9]+\"\/>/)    
     id=$i 
    else if($i ~ /<DATE V=\"[0-9]+ [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\"\/>/) 
     date=$i 
    } 
    if(!id) next 
    ids[NR]=id 
    if(id in recs && date < newest[id]) next 
    recs[id]=$0; newest[id]=date; order[id]=NR 
    } 
END { 
    asort(order) 
    for(i=1; i<=length(order); ++i) print recs[ids[order[i]]] 
    printf("\n") 
} 
'                   

提供的樣本輸入輸出

<BLOCK> 
<TEXT/> 
<ID V="4444"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20160101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<ID V="333"/> 
<DATE V="20120101 00:00:00"/> 
<TEXT/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<TEXT/> 
<ID V="22"/> 
<TEXT/> 
<DATE V="20151231 00:00:00"/> 
</BLOCK> 

<BLOCK> 
<TEXT/> 
<ID V="7777777"/> 
<TEXT/> 
<TEXT/> 
<DATE V="20130101 00:00:00"/> 
<TEXT/> 
</BLOCK>                  
+0

感謝您的解決方案!它適用於一個小問題,即「在文件結尾處沒有換行符」我標記了Ed Morton的解決方案,因爲它適用於所有awk版本。我感謝你的努力!祝你今天愉快! – Lev

+0

感謝您的嘗試 - 添加了最終的換行符 – pakistanprogrammerclub