【问题标题】:sed: How to add a key/value pair at the end of the section in INI filesed:如何在 INI 文件的部分末尾添加键/值对
【发布时间】:2015-02-05 22:48:50
【问题描述】:

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

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

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

对于情况(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]

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

【问题讨论】:

  • 你能发布预期的输出吗?
  • 为什么会有\n ?键值对之间没有换行符吗?
  • 我想在键/值对之间插入换行符
  • 您选择在需要时添加\n(部分缺失)或其他条件?事实上,我们是否可以假设该部分以 \n 结尾,或者一个新的方括号部分或 EOF
  • 考虑您有不同的 INI 文件编写方式。有些人倾向于在键/值对之间放置换行符,而有些人则没有(它可能在一个部分的末尾和下一部分的开头之前有多个换行符)。所以主要思想是匹配最后一次出现的键/值对而不是匹配换行符。

标签: regex bash sed ini


【解决方案1】:

为了完整起见(并且因为 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 替换为它,打印它,将模式空间交换回,打印它,交换保留空间并删除它(现在 section1 之后的行在保留缓冲区中,但我们不再关心它了)。其余时间,模式空间只是被交换回来并打印,所以从那里它就像cat一样工作。

编辑:NeronLeVelu 指出,这只有在[section1] 之后有一个部分可以匹配模式范围时才有效。如果不能保证这一点,则需要对最后一行进行特殊处理,以防此时尚未处理 section1。这样做可以:

$ {
  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 脚本变得这么长,就值得考虑其他选项。

【讨论】:

  • 它是否适用于部分结尾? (不能在这里测试,没有 GNU sed)
  • 你的意思是第1节是最后一节吗?你说得有道理。给我一分钟。
【解决方案2】:

你可以使用这个 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]

【讨论】:

  • 它抱怨“awk: line 1: runaway regular expression /[.*]{p=0 ...”
  • @anubhava 错字:正则表达式末尾有(不是:)缺少的“/”。
  • 我认为这只是滑落的 '/' 因为 mawk 非常标准......(没有太多扩展,但非常接近标准)
  • 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.ini 命令只要 ini 文件有一个名为 [Section1] 的部分就可以工作。
  • @chesschi toupper 他们俩toupper($1)==toupper(sec)
【解决方案3】:

另一种 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 打印所有行

【讨论】:

    【解决方案4】:
    sed -i '/^\[section1]/,/^\[/ {
       $ a\
    key10=value10
       /^\[section1]/ !{
          /[^=#]\{1,\}=/ !i\
    key10=value10
          }
       }' YourFile
    
    • 模式的posix版本,所以在GNU sed上添加--posix
    • 更改 [section1] 名称的 2 次出现以选择想要的部分
    • 为您的键/值对等体调整 key10=value10 的 2 行
    • 添加到“第一”行之后的部分中,在一个部分中具有非键/值对等体(以及最后一行的情况,即所选部分)

    【讨论】:

      【解决方案5】:

      我建议不要使用 sed 发明一个几乎不圆且易碎的轮子,而是使用专门用于操作 ini 文件的dedicated program,它非常易于使用:

      $ 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
      
      
      
      $
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-14
        • 1970-01-01
        • 2012-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多