2016-07-14 60 views
1

專家,如何分隔字段並用awk

追加文本我有一個XML文件中的以下文本(將有20000行的文件)。

<record record_no = "1" error_code="101">&quot;21006041&quot;;&quot;28006041&quot;;&quot;34006211&quot;;&quot;43&quot;;&quot;101210-0001&quot; 

以下是我需要每行結果並追加到新文件。

"21006041";"28006041";"34006211";"43";"101210-0001";101 

這是我需要做的,以獲得上述結果。

  • 我取代&quot;與「
  • 刪除<record record_no = "1" error_code="
  • 獲取文本101(它可以在這個位置上的任何值)
  • 追加到最後。

這裏是什麼我一直在嘗試。

BEGIN { FS=OFS=";" } 
     /<record/ { 
      gsub(/&quot;/,"\"") 
      gsub(/&apos;/,"") 
       gsub(/.*="|">.*/,"",$1) 
$(NF+1)=$1; 
$1=""; 
print $0; 
} 
+0

使用像XMLStarlet命令行XML解析器和做正確? – Kusalananda

回答

1

這應該有所斬斷。

awk -F'">' -v OFS=';' '{gsub(/<record record_no = \"[0-9]+\" error_code="/,""); gsub(/&quot;/,"\""); print $2,$1}'

的策略是:

  1. 在關閉xml元素的字符">
  2. 除去包括屬性名稱僅留下誤差的XML元素的第一比特分割字符串碼。
  3. 全部替換&quot; xml實體與"
  4. 以相反的順序打印兩個FS部分。

使用以下數據生成腳本進行測試。該腳本將生成帶有隨機長度記錄的500x20000行文件,其中一些文件的值爲破折號。

#!/bin/bash 
recCount=0 
for h in {1..500}; 
do 
    for i in {1..20000}; 
    do 
     ((recCount++)) 
     error=$((RANDOM % 998 + 1)) 
     record="<record record_no = "'"'"${recCount}"'"'" error_code="'"'"${error}"'"'">" 
     upperBound=$((RANDOM % 4 + 5)) 
     for ((k=0; k<${upperBound}; k++)); 
     do 
      randomVal=$((RANDOM % 99999999 + 1)) 
      record+="&quot;${randomVal}" 
      if [[ $((RANDOM % 4)) == 0 ]]; 
      then 
       randomVal=$((RANDOM % 99999999 + 1)) 
       record+="-${randomVal}" 
      fi  
      record+="&quot;" 
      if [[ $k != $((${upperBound} - 1)) ]]; 
      then 
       record+=";" 
      fi  
     done; 
     echo "${record}" >> "file-${h}.txt" 
    done; 
done; 

在我的筆記本電腦上,我得到以下性能。

$ time cat file-*.txt | awk -F'">' -v OFS=';' '{gsub(/<record record_no = \"[0-9]+\" error_code="/,""); gsub(/&quot;/,"\""); print $2,$1}' > result 

real 0m18.985s 
user 0m17.673s 
sys 0m2.697s 

作爲一個額外的好處,這裏是「等價」命令在sed: sed -e 's|\(&quot;\)|"|g' -e 's|^.*error_code="\([^>]\+\)">\(.\+\).*$|\2;\1|g'

慢得多,儘管該戰略是一致的。使用兩個表達式。首先用"替換所有&quot; xml實體。最後在>之後分組所有字符(。+)。顯示想起圖案以相反的順序\2;\1

計時統計:

$ time cat file-* | sed -e 's|\(&quot;\)|"|g' -e 's|^.*error_code="\([^>]\+\)">\(.\+\).*$|\2;\1|g' > result.sed 

real 5m59.576s 
user 5m56.136s 
sys 0m9.850s 
+0

Donavan,這很好,但如果我必須處理500個文件和每個包含20K記錄的文件。此解決方案是否可以更快地工作? – user2570205

+0

它是否必須是'awk'?怎麼樣'sed'。 這條命令對於20K條記錄在一秒鐘內運行: 'sed -e's/\(" \)/「/ g'-e's | ^。* error_code = \([^>] \ + \)> \(。\ + \)。* $ | \ 2; \ 1 | g'' – Donovan

+0

我完全修改了我的答案,以更好地適合問題500個文件* 20000行的上下文。 – Donovan

0

這是太厚了:

$ awk -F"&quot;+" -v OFS='";"' -v dq='"' '{gsub(/^.*="|">$/,"",$1);print dq""$2,$4,$6,$8,$10dq";"$1}' test.in 
"21006041";"28006041";"34006211";"43";"101210-0001";101 
+0

感謝您查看它,但是NF不是恆定的。 – user2570205