sed:如何在INI文件中的部分末尾添加键/值对

时间:2014-12-08 09:51:42

标签: regex bash sed ini

为了在INI文件的部分末尾添加一个键/值对,我想:

  1. 找到匹配部分名称并检查下一个方括号(即^ [)是否可用。如果是,则在这些行中找到最后一次出现的键/值对(即从匹配节名称到下一个方括号)。在最后一次出现的键/值对的末尾插入键/值对。

  2. 如果下一个方括号不可用,则将键/值对附加到文件的末尾。

  3. 对于情况(1),我可以使用sed检查下一个方括号是否可用,即

    sed -i -e "/^\[$section\]/,/^\[.*\]/ ..."
    

    但是,我不知道如何匹配key = value的最后一次出现。匹配最后一次出现的原因(而不是在下一个方括号之前添加ke / value对)是因为我可以选择在INI文件中的新键/值对之前引入换行符。例如,如果我想添加key10 = value10

    [section1]
    ...
    key8=value8
    key9=value9
    \n (could be multiple newline)
    [section2]
    

    变成

    [section1]
    ...
    key8=value8
    key9=value9
    \n (optional)
    key10=value10
    \n (could be multiple newline)
    [section2]
    

    请您能否建议我如何实现这一目标?

5 个答案:

答案 0 :(得分:2)

为了完整起见(因为sed hackery是令人心旷神怡的乐趣),你可以用这样的sed来做:

/^\[section1\]/,/^\[/ {
  x
  /^$/ !{ x; H }
  /^$/  { x; h }
  d
}
x
/^\[section1\]/ {
  s/\(\n\+[^\n]*\)$/\nkey10=value10\1/
  p
  x
  p
  x
  d
}
x
p

将其放入文件中,例如foo.sed,然后运行sed -n -f foo.sed foo.ini。或者,如果您愿意,

sed '/^\[section1\]/,/^\[/ { x; /^$/ !{ x; H }; /^$/ { x; h; }; d; }; x; /^\[section1\]/ { s/\(\n\+[^\n]*\)$/\nkey10=value10\1/; p; x; p; x; d }; x' foo.ini

......似乎已经不够难读了。

使用保持缓冲区很有趣;第一位匹配从[section1]到下一节开头的所有行,然后将所有行放入保持缓冲区(并在那里突破)。如果我们不在那个部分,其余的交换保持缓冲区,检查section1是否在其中,如果是,在适当的位置将key10=value10替换为它,打印它,交换模式空间,打印它,将保留空间重新交换并删除它(现在第1节之后的行在保持缓冲区中,但我们不再关心它了)。剩下的时间,模式空间只是换回并打印出来,所以从那里开始就像cat一样。

编辑:NeronLeVelu指出,只有在[section1]之后有一个模式范围可以匹配的部分时,这才有效。如果不能保证,那么在第1部分尚未处理的情况下需要对最后一行进行特殊处理。这可以做到:

$ {
  H
  x
  /^\[section1\]/ {
    s/\n\+$//
    a key10=value10
    x
  }
  x
  p
  d
}
/^\[section1\]/,/^\[/ {
  x
  /^$/ !{ x; H }
  /^$/  { x; h }
  d
}
x
/^\[section1\]/ {
  s/\(\n\+[^\n]*\)$/\nkey10=value10\1/
  p
  x
  p
  x
  d
}
x
p

这将处理最后一行与其他行不同;它会将它附加到保持缓冲区,检查section1是否还需要处理,如果是,则追加key10=value10并交换回来,然后再次交换并打印出模式空间。这个令人困惑的分流位意味着如果section1仍在保持缓冲区中,则打印保持缓冲区,否则打印最后一行。

到目前为止,我不得不指出,一旦sed脚本变得这么长,就值得考虑其他选择。

答案 1 :(得分:1)

你可以使用这个awk:

awk -v sec='[section1]' -v kv='key10=value10' 'p && $1~/\[[^]]*\]/{p=0; print kv} 
           $1==sec{p=1} END{if (p) print kv} 1' file
[section1]
...
key8=value8
key9=value9

key10=value10
[section2]

答案 2 :(得分:0)

另一种hacky awk方式

awk -v k='key10=value10' '($1=="[section1]"||x&&/^\[/)&&!(x=!x){print k}
                           END{if(x)print k}1' file

检查字段1是否等于[section1]或x是否不是0 如果其中任何一个为真,则将x设置为x的倒数并检查它是否为0 如果为0则打印键值对。
最后如果x仍然设置,则打印键值对 1打印所有行

答案 3 :(得分:0)

sed -i '/^\[section1]/,/^\[/ {
   $ a\
key10=value10
   /^\[section1]/ !{
      /[^=#]\{1,\}=/ !i\
key10=value10
      }
   }' YourFile
  • 模式的posix版本,所以在GNU sed上添加--posix
  • 更改[section1]名称的出现次数以选择所需部分
  • 为您的key / value peer
  • 调整2行key10=value10
  • 在“第一”行之后添加到部分中的非键/值对等项(并且最后一行的情况也是所选部分)

答案 4 :(得分:0)

我建议使用专门用来操作ini文件的dedicated program而不是用sed发明一个几乎圆而脆弱的轮子,而不是简单易用:

$ cat test.conf | tee test.conf.orig
[section1]
##...
key8=value8
key9=value9





[section2]
abc=123
$ crudini --set test.conf section1 key10 value10
$ diff -u test.conf.orig test.conf
--- test.conf.orig      2017-10-10 20:59:55.205678995 +0200
+++ test.conf   2017-10-10 21:00:00.831477388 +0200
@@ -2,6 +2,7 @@
 ##...
 key8=value8
 key9=value9
+key10 = value10



$