2012-02-25 28 views
8
sed -e '/XXXX/,+4d' fv.out 

我必須在文件中找到特定的模式,並且同時刪除上面的5行和下面的4行。我發現上面的那一行刪除了包含模式和它下面四行的行。刪除相對於包含模式的行的n1個之前的行和n2行

sed -e '/XXXX/,~5d' fv.out 

在sed手冊中給出〜表示模式後面的行。但是當我嘗試它時,它是被刪除的模式之後的行。

那麼,如何刪除上面5行和下面同時包含該模式的行下面4行?

回答

5

一個使用sed,假設模式無法關閉對方的方式:

內容的

script.sed

## If line doesn't match the pattern... 
/pattern/ ! { 

    ## Append line to 'hold space'. 
    H 

    ## Copy content of 'hold space' to 'pattern space' to work with it. 
    g 

    ## If there are more than 5 lines saved, print and remove the first 
    ## one. It's like a FIFO. 
    /\(\n[^\n]*\)\{6\}/ { 

     ## Delete the first '\n' automatically added by previous 'H' command. 
     s/^\n// 
     ## Print until first '\n'. 
     P 
     ## Delete data printed just before. 
     s/[^\n]*// 
     ## Save updated content to 'hold space'. 
     h 
    } 

### Added to fix an error pointed out by potong in comments. 
### ======================================================= 
    ## If last line, print lines left in 'hold space'. 
    $ { 
     x 
     s/^\n// 
     p 
    } 
### ======================================================= 


    ## Read next line. 
    b 
} 

## If line matches the pattern... 
/pattern/ { 

    ## Remove all content of 'hold space'. It has the five previous 
    ## lines, which won't be printed. 
    x 
    s/^.*$// 
    x 

    ## Read next four lines and append them to 'pattern space'. 
    N ; N ; N ; N 

    ## Delete all. 
    s/^.*$// 
} 

運行,如:

sed -nf script.sed infile 
+0

謝謝,我正在尋找的東西(我有至少20行分隔的模式發生)。 – 2012-02-25 12:55:56

1

這個想法是讀取5行而不打印它們。如果您發現該圖案,請刪除未打印的線條和下面四行。如果沒有找到圖案,請記住當前行並打印第一條未打印線。最後,打印什麼是未打印的。

sed -n -e '/XXXX/,+4{x;s/.*//;x;d}' -e '1,5H' -e '6,${H;g;s/\n//;P;s/[^\n]*//;h}' -e '${g;s/\n//;p;d}' fv.out 

當然,這隻有在文件中出現一次模式時纔有效。如果你有很多,你需要在找到你的模式後閱讀5個新行,如果你再次在這些行中有你的模式,它會變得複雜。在這種情況下,我認爲sed不是正確的工具。

+0

感謝。它在第一次發生時有效。但是,我有很多這種模式。可能是我必須把它放在一個循環中,這樣grep纔不會再出現該模式。除了使用sed以外,你還有什麼建議? – 2012-02-25 11:25:16

+0

我正在研究awk解決方案。在我看來,它更好,因爲更容易理解和維護。 – jfg956 2012-02-25 11:54:31

1

這可能會爲你工作:

sed 'H;$!d;g;s/\([^\n]*\n\)\{5\}[^\n]*PATTERN\([^\n]*\n\)\{5\}//g;s/.//' file 

或該:

awk --posix -vORS='' -vRS='([^\n]*\n){5}[^\n]*PATTERN([^\n]*\n){5}' 1 file 

更有效的SED溶液:

sed ':a;/PATTERN/,+4d;/\([^\n]*\n\)\{5\}/{P;D};$q;N;ba' file 
+0

謝謝。它完美的作品。但是,我有一些巨大的文件,它佔用了很多時間。 – 2012-02-25 12:54:37

+0

@PopulationXplosive我已經添加了一個awk解決方案。它可能會更快。 – potong 2012-02-25 19:26:19

+0

謝謝。 awk解決方案也佔用大量時間。但新的sed解決方案相當快速。真的很不錯。 – 2012-02-27 10:21:08

2

的溶液使用awk

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    nlines == 5 { print lines[NR%5]; nlines-- } 
    lines2del == 0 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

更新:

這是SC ript解釋:

  • 我記得數組lines的最後5行使用旋轉索引(NR%5; NR是創紀錄的數字;在這種情況下線)。
  • 如果我發現在當前行的模式($0 ~ "XXXX; $0是當前記錄:在這種情況下的直線;作爲擴展正則表達式匹配運算符和~),我重新閱讀和記行數我有5行刪除(包括當前行)。
  • 如果我已經讀了5行,我打印當前行。
  • 如果我沒有行刪除(這也是真的,如果我讀了5條線路,我把當前行的緩衝區,增加行數。注意如何行的數目減少,然後遞增,如果一行打印
  • 如果需要刪除行,我不會打印任何內容並減少要刪除的行數
  • 在腳本結尾,我打印出數組中的所有行。

我的腳本的原始版本是下面的,但我結束了它優化到以上版本:

awk '$0 ~ "XXXX" { lines2del = 5; nlines = 0; } 
    lines2del == 0 && nlines == 5 { print lines[NR%5]; lines[NR%5] } 
    lines2del == 0 && nlines < 5 { lines[NR%5] = $0; nlines++ } 
    lines2del > 0 { lines2del-- } 
    END { while (nlines-- > 0) { print lines[(NR - nlines) % 5] } }' fv.out 

awk是一個偉大的工具!我強烈建議你在網上找到一個教程並閱讀它。一個重要的事情:awk擴展正則表達式ERE)的作品。其語法是標準正則表達式RE)在sed使用略有不同,但都可以用稀土做可以與ERE完成。

+0

謝謝。這是工作。但是,我是awk的絕對初學者。那麼,你能解釋一下腳本嗎?另外,你如何分別修復n1和n2?在我看來,在這裏你已經取得了n1 = n2。 – 2012-02-25 12:54:00

+0

我不明白你的n1/n2問題。解釋腳本是非常前沿的,我正在更新這篇文章。 – jfg956 2012-02-25 18:26:49

+0

謝謝。我想知道如何刪除下面4行(n2)模式。因爲我沒有awk的經驗,所以我認爲你把(n2)之後和(n1)之前的行數設爲相同。我非常困惑。我認爲如果我學習了awk也會很棒。 – 2012-02-27 10:40:45

1

如果你很高興並將結果輸出到一個文件,而不是標準輸出,vim可以非常有效地做到這一點:

vim -c 'g/pattern/-5,+4d' -c 'w! outfile|q!' infile 

vim -c 'g/pattern/-5,+4d' -c 'x' infile 

編輯就地文件。

相關問題