簡體   English   中英

sed中的匹配后如何替換文本塊

[英]how to replace a block of text after a match in sed

sed ,我想在匹配后替換多行文本塊,例如,在匹配“ foo”之后,假設其行號為0。我想將文本塊從-3行替換為+如圖5所示,也就是說,文本行之間的文本框由另一個文本塊bar1\\nbar2匹配行之后的第五行和匹配行之后的第五行bar1\\nbar2 我希望能夠在兩種情況下執行此操作:

1)在替換的塊之后保留匹配行; 2)刪除匹配的行以及那些行-3和+5。

請幫我。

謝謝。

多次使用N來讀取八行,然后就可以像將它們串聯起來一樣進行匹配-sed將在模式中識別\\ n,因此在單個零件(行)上的工作很容易。

例:

$ echo '1
2 oooh
3
4
match
5
6
7
8
9 oooh
10 ' | sed ': label; N; s/[^\n]*\n[^\n]*\n[^\n]*\nmatch\n[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n/bar1\nbar2/; T label'

讀取直到進行替換(T)。 由於您可能要捕獲多個塊,因此將T更改為b ,它將始終分支。 如果還沒有自動發生。

要求的簡短格式:

echo '1
2 oooh
3
4
match
5
6
7
8
9 oooh
10 ' | sed ': label; N; s/\([^\n]*\n\)\{3\}match\n\([^\n]*\n\)\{5\}/bar1\nbar2/; T label'

首先,我們定義一個自記錄的sed標簽,稱為“標簽”。 它使我們能夠跳入其他代碼-將其視為“ goto”語句。 由於它是一開始的,因此跳到那里將重復所有sed命令。 我們確實有一個單一目的N ,它讀取下一行並將其附加到模式空間。 重復一遍又一遍,因此我們可以獲取要檢查(和刪除)的上下文行,並在它們上運行單個正則表達式。 這是以下s語句的工作,該語句首先查找上一個模式組( \\([^\\n]*\\n\\) )的3個重復( \\{3\\} ),該模式組是任何類型的行。 然后,它會在下一行中查找您要查找的標記字符串(在此示例中為match )以及另外5行。 如果此多行模式匹配,則會進行替換,並且作業即將完成。 我們需要使用循環,否則整個表達式將針對每行單獨運行,一直讀下去,而不做我們想要做的事情-批量讀取行。

這可能有效(GNU sed):

seq 31|sed 's/5/& match/' >/tmp/file
sed ':a;$q;N;s/\n/&/3;Ta;/match/!{P;D};:b;$bc;N;s/\n/&/8;Tb;:c;s/.*/bar1\nbar2/' /tmp/file
1
bar1
bar2
11
bar1
bar2
21
bar1
bar2
31
sed ':a;$q;N;s/\n/&/3;Ta;/match/!{P;D};h;s/\([^\n]*\n\)*\([^\n]*match[^\n]*\).*/\2/;x;:b;$bc;N;s/\n/&/8;Tb;:c;s/.*/bar1\nbar2/;G' /tmp/file
1
bar1
bar2
5 match
11
bar1
bar2
15 match
21
bar1
bar2
25 match
31

說明:

命令分為兩半:

  1. 前半部分保持3行移動窗口。
  2. match結束后,再追加5行。

詳情如下所示:

  • :a是循環占位符
  • 文件結尾的$q打印圖案空間(PS)內的所有行。
  • N將下一行附加到PS
  • s/\\n/&/3單獨替換第3個換行符。 此計數設備用於檢查PS中是否有3條線。
  • Ta如果先前的替換失敗循環到循環占位符a
  • /match/!{P;D}查找match ,如果match失敗,則打印到第一行,然后刪除該行及其換行符(這將調用一個新循環)。
  • :b是循環占位符NB,此時已找到匹配項。
  • $bc如果文件結束分支轉發到占位符c
  • N將下一行附加到PS
  • s/\\n/&/8自行替換第8個(換行符之前的3,5之后的字符)。 此計數設備,用於檢查是否在PS上附加了5行
  • 如果先前的替換失敗循環到循環占位符b則為Tb
  • :c是循環占位符
  • s/.*/bar1\\nbar2/用所需的字符串替換PS。

第二個襯線制作match線的副本,並將其附加到替換的字符串。

替代解決方案:

sed -r ':a;$!N;s/[^\n]*/&/9;$!Ta;/^([^\n]*\n){3}([^\n]*match[^\n]*)\n.*/!{P;D};c\bar1\nbar2' file

sed -r ':a;$!N;s/[^\n]+/&/9;$!Ta;/^([^\n]*\n){3}([^\n]*match[^\n]*)\n.*/!{P;D};s//\bar1\nbar2\n\2/' file

在第二種情況下,使用GNU sed一種方法是,盡管看起來有點復雜(已充分評論):

假設infile具有以下內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

script.sed內容:

## From first line until a line that matches the pattern (number ten in 
## this example), save lines in buffer and print each one when there are
## more than three lines between one of them and the line with the pattern
## to search.
0,/10/ {

        ## Mark 'a'
        :a

        ## If line matches the pattern break this loop.
        /10/ {
                bb
        }

        ## Until the pattern matches, if more than three lines (checking '\n') are
        ## saved, print the oldest one and delete it, because I only want to save last
        ## three.
        /\(\n[^\n]*\)\{3\}/ {
                P
                D
        }

        ## Append next line to pattern space and goto mark 'a' in a loop.
        N
        ba
}

## It should never match (I think), but it's a sanity check to avoid the
## following mark 'b'.
bc

## Here we are when found the line with the pattern, so read next five six
## lines and delete all of them but the sixth. If end of file found in this
## process none of them will be printed, so it seems ok.
:b
N;N;N;N;N
N
s/^.*\n//

## Here we are after deleting both '-3' and '+5' lines from the pattern matched,
## so only is left to print the remainder of the file in a loop.
:c
p
N
s/^.*\n//
bc

考慮到在第五行和第十一行代碼中都是10模式,就可以像這樣運行它。 根據您的需要進行更改。 在我的示例中,應刪除第7,8,9,10,11,12,13,14,15行:

sed -nf script.sed infile

具有以下輸出:

1
2
3
4
5
6
16
17
18
19
20

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM