简体   繁体   English

用 sed 替换多行字符串

[英]Replace multiline string with sed

I have a file that's basically an INI/CFG file the looks like this:我有一个基本上是 INI/CFG 文件的文件,如下所示:

[thing-a]
attribute1=foo
attribute2=bar
attribute3=foobar
attribute4=barfoo

[thing-b]
attribute1=dog
attribute3=foofoo
attribute4=castles

[thing-c]
attribute1=foo
attribute4=barfoo

[thing-d]
attribute1=123455
attribute2=dogs
attribute3=biscuits
attribute4=1234

Each 'thing' has a set of attributes that could include all the same ones or a subset there of.每个“事物”都有一组属性,可以包括所有相同的属性或其中的一个子集。

I am trying to write a small bash script that will replace the attributes for 'thing-c' with a predefined block $a1, $a2 & $a3 are generated elsewhere in the wider script:我正在尝试编写一个小的 bash 脚本,它将用预定义块替换“thing-c”的属性 $a1、$a2 和 $a3 在更广泛的脚本的其他地方生成:

NEW_BLOCK="[thing-c]
attribute1=${a1}
attribute2=${a2}
attribute3=${a3}"

I can find the right block with sed like this:我可以像这样找到带有 sed 的正确块:

THING_BLOCK=$(sed -nr "/^\[thing-c\]/ { :l /^\s*[^#].*/ p; n; /^\[/ q; b l; }" ./myThingFile)

I'm not sure if i've gone down a rabbit hole or what with this and I'm pretty sure there is a better way of doing it.我不确定我是不是掉进了兔子洞,或者这是怎么回事,我很确定有更好的方法可以做到这一点。

I'm wanting to do what is:我想做的是:

sed "s/${THING_BLOCK}/${NEW_BLOCK}/"

But I can't quite figure out the multiline aspect to this and I'm not sure what the best route to take is.但是我不太清楚这个的多线方面,我不确定最好的路线是什么。

Is there a way to do this sort of multiline find and replace with sed (or a better way with bash)有没有办法用 sed 进行这种多行查找和替换(或者使用 bash 的更好方法)

Is there a way to do this sort of multiline find and replace...有没有办法进行这种多行查找和替换......

Yes there is indeed a better way, albeit using awk :是的,确实有更好的方法,尽管使用awk

awk -v blk="$NEW_BLOCK" -v RS= '{ORS = RT} $1 == "[thing-c]" {$0 = blk} 1' file

Using -v RS= we use an empty record separator that splits records in input file on each new line.使用-v RS=我们使用一个空的记录分隔符,在每个新行上拆分输入文件中的记录。

Another awk. Store the replacement to file2 and:另一个 awk。将替换存储到file2并:

$ awk -v RS="" '
NR==FNR {
    b=$0
    next
}
$1~/thing-c/ {
    $0=b
}
{
    print (++c==1?"":ORS) $0
}' file2 file1

Output: Output:

[thing-a]
attribute1=foo
attribute2=bar
attribute3=foobar
attribute4=barfoo

[thing-b]
attribute1=dog
attribute3=foofoo
attribute4=castles

[thing-c]
attribute1=${a1}
attribute2=${a2}
attribute3=${a3}

[thing-d]
attribute1=123455
attribute2=dogs
attribute3=biscuits
attribute4=1234

When you want to use sed (IMHO awk is better here), you must have "nice" data (no special characters that sed will try to handle and [ inside block thing-3 ).当您想使用sed (恕我直言, awk在这里更好)时,您必须拥有“不错”的数据(没有sed将尝试处理的特殊字符和[ inside block thing-3 )。
I tested with我测试过

read -d '' -r NEW_BLOCK <<END
[thing-c]
attribute1=${a1}
attribute2=${a2}
attribute3=${a3}
END

For my solution I first need to replace newlines in $NEW_BLOCK with the two characters \n .对于我的解决方案,我首先需要用两个字符\n替换$NEW_BLOCK中的换行符。

echo "This is the replacement string: ${NEW_BLOCK//$'\n'/\\n}"

With the "multi-line" option "-z" you can do使用“多行”选项“-z”你可以

sed -rz "s/\[thing-c\][^[]*/${NEW_BLOCK//$'\n'/\\n}\n\n/" myThingFile

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM