[英]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
說明:
命令分為兩半:
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.