在匹配的块之前插入代码块。

huangapple go评论54阅读模式
英文:

SED INSERT block of code BEFORE matched block

问题

INSERT BEFORE:

sed '/Categ         #n/{N;N;s/Categ         #n\nItemName      1/&\n**new_line1**\n**new_line2**\n**new_line3**/}' input.txt

APPEND AFTER:

sed '/ItemName      2/{N;s/\(ItemName      2\)\(.*\)/\n**new_line1**\n**new_line2**\n**new_line3**/}' input.txt
英文:

I have a text file I want to edit during a script excecution.
I want to pass from the text on left to the text on right using sed insert:

INSERT BEFORE:
from:

[...]
[ placeholder ]
Categ         #n
ItemName      1
[...]
[ placeholder ]
Categ         #n
ItemName      2
[...]

to:

[...]
[ placeholder ]
Categ         #n
ItemName      1
[...]
**new_line1**        <<<--- INSERT BEFORE
**new_line2**        |||
**new_line3**        |||
[ placeholder ]      <<<--- 3-lines match
Categ         #n     |||
ItemName      2      |||
[...]

I have tried to adapt sed append lines to sed insert.
The appending works, the insert does not.
The match to append was 2 lines one after the other.
In the insert, the match it is 3 lines and I tried to match the first and the last, as I don't know how to match the entire 3-lines block.

APPEND AFTER:

sed -e '/^\[ placeholder \]\/ItemName      2/a \\nnew_line1\nnew_line2\nnew_line3' input.txt

and it works from:

[...]
[ placeholder ]
ItemName      1
[...]
[ placeholder ]
ItemName      2
[...]

to:

[...]
[ placeholder ]
ItemName      1
[...]
[ placeholder ]     <<<--- 2-lines match
ItemName      2     |||
**new_line1**       <<<--- APPENDED AFTER
**new_line2**       |||
**new_line3**       |||
[...]

I have tried to use the syntax examples in this post, but with no success.
The sed - Stream Editor

答案1

得分: 0

问题会变得简单得多,如果文件是倒着读的。

可以使用sed解决方案,但awk非常直观:

tac input.txt |
awk '
    # 将每一行输入复制到输出
    { print }

    # 查找所需的项目名称行
    #   如果找到,改变状态并跳过剩余的测试(无法匹配)
    $1=="ItemName" && $2==2 { s = 1; next }

    # 如果我们到达这里,查找占位符行
    #   如果找到,追加新行并重置状态
    s && /\[ placeholder \]/ {
        print "new3"
        print "new2"
        print "new1"
        s = 0
    }
' | tac > output.txt
英文:

The problem becomes much simpler if the file is read backwards.

A sed solution is possible but awk is very straightforward:

tac input.txt |
awk '
    # copy every input line to output
    { print }

    # look for desired itemname line
    #   if found, change state and
    #   skip remaining tests (which can't match)
    $1=="ItemName" && $2==2 { s = 1; next }

    # if we get here, look for placeholder line
    #   if found, append new lines and reset state
    s && /\[ placeholder \]/ {
        print "new3"
        print "new2"
        print "new1"
        s = 0
    }
' | tac > output.txt

答案2

得分: 0

如果您的输入中不包含NUL字节,您可以尝试使用GNU sed执行以下操作:

$ from='[ 占位符 ]\nCateg         #n\nItemName      2\n'
$ to='**新行1**\n**新行2**\n**新行3**\n'
$ sed -Ez "s/(^|\n)($from)/$to/" input.txt
[...]
[ 占位符 ]
Categ         #n
ItemName      1
[...]
**新行1**
**新行2**
**新行3**
[ 占位符 ]
Categ         #n
ItemName      2
[...]

使用-z选项将输入视为一组以零字节(ASCII NUL字符)终止的行,而不是以换行符终止。如果输入不包含NUL字节,这将允许一次性处理整个文件。当然,您必须正确转义fromto中的特殊字符。

英文:

If your input does not contain NUL bytes you could try the following with GNU sed:

$ from='\[ placeholder \]\nCateg         #n\nItemName      2\n'
$ to='**new_line1**\n**new_line2**\n**new_line3**\n'
$ sed -Ez "s/(^|\n)($from)/$to/" input.txt
[...]
[ placeholder ]
Categ         #n
ItemName      1
[...]
**new_line1**
**new_line2**
**new_line3**
[ placeholder ]
Categ         #n
ItemName      2
[...]

With the -z option the input is treated as a set of lines, each terminated by a zero byte (the ASCII NUL character) instead of a newline. If the input does not contain NUL bytes this allows to process the entire file at once. Of course you must properly escape special characters in from and to.

答案3

得分: 0

以下是已翻译的代码部分:

A loop probably makes sense to accumulate the block into the pattern space:

    sed '/placeholder/{:l /ItemName/!{N;bl};/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file 

Note that if `ItemName` is always two lines after `placeholder` then this simpler method also works:

    sed '/placeholder/{N;N;/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file

There is also this complicated method of accumulating in the hold space rather than the pattern space:

    sed '/placeholder/,/ItemName/{/placeholder/h;//!H;/ItemName/!d;//g;/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file

All methods produce this output:

    [...]
    [ placeholder ]
    Categ         #n
    ItemName      1
    [...]
    new1
    new2
    new3
    [ placeholder ]
    Categ         #n
    ItemName      2
    [...]

这是你提供的代码的翻译部分。

英文:

A loop probably makes sense to accumulate the block into the pattern space:

sed '/placeholder/{:l /ItemName/!{N;bl};/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file 

Note that if ItemName is always two lines after placeholder then this simpler method also works:

sed '/placeholder/{N;N;/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file

There is also this complicated method of accumulating in the hold space rather than the pattern space:

sed '/placeholder/,/ItemName/{/placeholder/h;//!H;/ItemName/!d;//g;/ItemName.*2/s/^/new1\nnew2\nnew3\n/}' file

All methods produce this output:

[...]
[ placeholder ]
Categ         #n
ItemName      1
[...]
new1
new2
new3
[ placeholder ]
Categ         #n
ItemName      2
[...]

The simpler method is simply using N to append more lines into the pattern space before it's printed.

The loop adds a label :l and b branches to that label until ItemName.

The hold space method uses h and H to hold, d to delete and not print, g to get. As well as // as shorthand to repeat same match.

I have been lazy with the matches and the lines inserted to make this all more readable. ItemName.*2 is especially lazy. Probably should be something like ItemName\s\+2\b

答案4

得分: 0

这可能适用于您(使用GNU sed和bash):

match='[ placeholder ]
Categ         #n
ItemName      2'
insert='**new_line1**\n**new_line2**\n**new_line3**'
sed '1N;N;/^'"${match}"'$/!{P;D};i\'"${insert}"' file
打开一个三行窗口,直到找到匹配项,然后插入所需的字符串,之后两行窗口将继续,直到文件末尾。
英文:

This might work for you (GNU sed and bash):

match=''\[ placeholder \]\nCateg         #n\nItemName      2'
insert='**new_line1**\n**new_line2**\n**new_line3**'
sed '1N;N;/^'"${match}"'$/!{P;D};i\'"${insert}" file

Open a three line window until a match occurs, then insert the required string and thereafter the two line window will continue until the end of file.

huangapple
  • 本文由 发表于 2023年7月13日 18:10:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76678234.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定