2012-10-01 47 views
0

給定一個排序的文件,像這樣:如何根據sed中前一行和當前行中的匹配刪除當前行?

AAA 1 2 3 
AAA 2 3 4 
AAA 3 4 2 
BBB 1 1 1 
BBB 1 2 1 

AAA 1 2 3 
BBB 1 1 1 

什麼用的sed實現這一目標的最佳途徑所需的輸出?

基本上,如果col以前一行的字段開頭,我該如何刪除它?其餘的數據必須保存在輸出中。

我想必須有一些方法來使用保持緩衝區,分支或測試命令來做到這一點。

回答

1

另一種方式:

awk '!($1 in a){print;a[$1]}' file 
+0

有趣,但不太什麼要求中,如果第六行是'AAA 5 6 7',問題會期望它被打印出來,但是你的代碼不會因爲'AAA'曾經被看到過。 (但是,由於這是公認的,誰也不知道。也許我誤解了這個問題) –

+0

但它確實工作,如果文件排序 – Dinedal

+0

他們是否排序? – 2012-10-01 19:19:26

1

這可能與AWK來實現:使用awk

$ gawk '{if (last != $1) print; last = $1}' in.txt 
AAA 1 2 3 
BBB 1 1 1 
0

也許有與sed一個更簡單的方法,但是:

sed ':a;N;/\([[:alnum:]]*[[:space:]]\).*\n\1/{s/\n.*//;ta};P;D' 

這將產生輸出

AAA 1 2 3 
BBB 1 1 1 

其不同之處在於這個問題,但說明相匹配:

如果山坳用相同的字段作爲前行開始,我怎麼刪除呢?

+0

輸出是錯誤的,更新。你的sed表達式只是返回每一行,儘管我的測試數據。 – Dinedal

+0

@Dinedal然後發佈您的測試數據。我使用了這個問題。如果它不能在你的系統上使用問題中的數據,那麼它是什麼系統? OS X,也許吧? –

+0

這是OS X.這可能是爲什麼,引擎不匹配或什麼的。 – Dinedal

0

這可能會爲你工作(GNU SED):

sed -r ':a;$!N;s/^((\S+\s).*)\n\2.*/\1/;ta;P;D' file 

或者只是:

sort -uk1,1 file 
0

一個使用GNU awk方式:

awk '!array[$1]++' file.txt 

結果:

AAA 1 2 3 
BBB 1 1 1 
0

使用的sed:

#!/bin/sed -nf 

P 

: loop 
s/\s.*// 
N 
/\([^\n][^\n]*\)\n\1/ b loop 

D 

首先,我們必須通過-n標誌的sed,因此將只打印什麼,我們告訴它。

我們首先打印帶有「P」命令的行,因爲第一行將始終打印,我們將強制sed在我們需要時執行此行。

現在我們將做一個循環。我們通過「:」命令定義了一個帶有起始標籤的循環(在這種情況下,我們將標籤命名爲「loop」),必要時我們用「b」命令(或「t」測試命令)。這個循環很簡單:

  1. 刪除一切,但第一場(更換什麼也沒有它後面的第一個空格字符和一切)
  2. 追加下一行(換行符將被包括在內)
  3. 檢查新行是否與我們隔離的字段開始。我們通過使用捕獲來做到這一點。捕獲被定義爲「子匹配」,匹配的輸入將被存儲到一個特殊的「變量」中,以數字方式按照存在的順序命名。我們指定使用括號捕獲逃脫backslased(開始與\(\)結束)。在這種情況下,我們將所有不是換行符的字符(即[^\n])都匹配到行尾。我們通過匹配至少一個非換行符後跟任意序列來完成此操作。這可以防止在換行符之前匹配空字符串。捕獲後,我們通過使用特殊變量\1,其中包含由第一捕獲匹配的輸入匹配換行符隨後捕獲的結果。如果成功,我們有一條重複第一個字段的行,所以我們用「b」分支命令跳回到循環的開始位置。
  4. 當我們退出循環,我們發現有一個不同的第一場線,所以我們必須準備輸入線和跳回腳本的開頭。這可以通過「D」delete-first-line-and-restart-script命令來完成。

這可以被縮短成一條線(注意,我們已經改名爲「循環」標籤到「A」):

sed -e 'P;:a;s/\s.*//;N;/\([^\n][^\n]*\)\n\1/ba;D'