【问题标题】:sed: change values of properties of an environment in a .yml filesed:更改 .yml 文件中环境属性的值
【发布时间】:2011-08-09 01:41:14
【问题描述】:

我有一个配置应用程序环境属性的 .yml 文件,如下所示:

env1:
  :prop1: "value1"
  :prop2: "value2"
        ...
  :propn: "valuen"

env2:
  :prop1: "value1"
  :prop2: "value2"
  :prop3: "value3"
        ...
  :propn: "valuen"

    ...

envn:
  :prop1: "value1"
  :prop2: "value2"
        ...
  :propn: "valuen"

我想制作一个具有以下界面的 bash 脚本:

$ change_env.sh <environment> <property> <new value> <file.yml>

例子:

$ change_env.sh env2 prop3 "this value was changed" file.yml

输出将是:

env1:
  :prop1: "value1"
  :prop2: "value2"
        ...
  :propn: "valuen"

env2:
  :prop1: "value1"
  :prop2: "value2"
  :prop3: "this value was changed"
        ...
  :propn: "valuen"

    ...

envn:
  :prop1: "value1"
  :prop2: "value2"
        ...
  :propn: "valuen"

我找到了这篇文章,但是我无法为我的情况做这件事。 Replace an XML element's value? Sed regular expression?

我也试过这个:(它失败了,因为改变了所有属性)

sed 's/\(:pro3:\).*/\1 "new value"/'

提前致谢! -- 洛伦科。

【问题讨论】:

    标签: regex parsing bash sed


    【解决方案1】:

    (非常好的第一篇文章!)

    试试这个

    cat change_env.sh
    
    #!/bin/bash
    # spec : change_env.sh <environment> <property> <new value> <file.yml>
    
    case ${#} in [!4] ) 
        echo "usage: change_env.sh <environment> <property> <new value> <file.yml>" 1>&2
        exit 1 
       ;; 
    esac
    
    env="$1" prop="$2" new="$3" file="$4"
    bakFile="${file}".bak
    mv "$file" "$bakFile"
    sed '/^'"${env}"'/,/^[   ]*$/{  # [ spaceChar tabChar ]
            /'"${prop}"'/s/\('"${prop}"'\)\(.*$\)/\1'"${new}"'/
        }' "$bakFile" > "$file"
    

    编辑

    注意,如果您希望输入值中包含空格,您需要修改脚本以引用所有变量(“$1”、“$2”...)。 (我现在已经这样做了,因为它是 shell 脚本的最佳实践)。

    /env/,/^[{space,tab}]*$/ 是 sed 的范围地址。它读取包含您的环境设置的文本块。我假设您的示例输入是正确的,并且每个 env 都由一个空行分隔。嗯...这将包括文件中的最后一个。

    ** 编辑**

    感谢@posdef 指出这个答案的一些问题。代码已更新以解决特定情况。

    即使在修复之后,我确实注意到给定的输入像

       change_env.sh env2 prop2 "new value" file.yml
    

    相关的输出是

         :prop2new value
    

    因此,如果不将额外的 : 和空格字符硬编码到替换中,这意味着您需要非常详细地调用 &lt;property&gt; 值和 &lt;new value&gt; ,即

       change_env.sh env2 ":prop2: " "\"new value\"" file.yml
       # note extra cruft-^^-----^^^--^^---------^^--------------
    

    相关输出

    env2:
      :prop1: "value1"
      :prop2: "new value"
      :prop3: "value3"
        ...
      :propn: "valuen"
    

    IHTH

    【讨论】:

    • 非常好!解决这个问题!!非常感谢!
    • 我不确定是不是只有我,但这个脚本对我来说不能正常工作:1)属性名称被改变而不是值;试图将名为“differences”的属性的值从 2 更改为 0 变为 0: 2。 2) 0: 2 行在输出文件中重复。
    • @posdef :感谢您指出该缺陷。它现在已修复,但我不能花时间尝试为一般情况制定解决方案,因此&lt;properties&gt; 搜索目标也必须准确&lt;new value&gt;。 (即 ":prop2: ""\"new value\"" 。祝大家好运。
    • @shellter 不用担心,只是提到其他人可能会像我一样偶然发现这个答案的潜在缺陷 :)
    【解决方案2】:

    我会使用 awk:

    #!/bin/sh
    
    if [ $# -ne 4 ]; then
        echo "usage: $0 env prop value file" >&2
        exit 1
    fi
    
    awk -F : -v env="$1" -v prop="$2" -v val=" \"$3\"" '
        BEGIN {OFS = FS}
        $1 == env {in_env = 1}
        NF == 0   {in_env = 0}
        in_env && $2 == prop {$3 = val}
        {print}
    ' "$4"
    

    【讨论】:

    • 很高兴知道使用 AWK 有一种简单的方法。谢谢!
    • 据我所知,这个脚本没有按预期工作,因为它不注意环境中项目的缩进。换句话说,条件in_env &amp;&amp; $2 == prop 永远不会成立,因为prop 通常位于新行中,这意味着它不会是$2,而是下一行中的$1。我会根据你的小修复添加一个答案
    • 不,我不使用缩进,我使用冒号作为字段分隔符。并且所有的“prop”都有一个前导冒号,这使得属性成为第二个字段。
    • @glennjackman 我明白了,一定错过了属性上的前导冒号,不熟悉该符号。为混乱道歉
    【解决方案3】:

    您至少需要一个前瞻断言。有很多方法可以做到这一点,但你应该使用知道如何解析它的东西。

    s/(\s*$env:\s*(?:(?!\s*[^\W_]+:)[^\n]*\s*)*\s*:$prop:[^\S\n]*")[^\n]*(")/$1$replacement$2/g

    在 perl 中进行了轻度测试:

    use strict;
    use warnings;
    
    my $str = '
    env1:
      :prop1: "value1"
      :prop2: "value2"
            ...
      :propn: "valuen"
    
    env2:
      :prop1: "value1"
            ...
      :prop2: "value2"
      :prop3: "value3"
      :propn: "valuen"
        ...
    
    envn:
      :prop1: "value1"
            ...
      :prop3: "value3"
      :propn: "valuen"
    ';
    
    my ($env, $prop, $replacement) = ('(?:env2|envn)', 'prop1', 'this changed');
    
    if ( $str =~ s/
        (
          \s*$env:\s*
          (?: (?!\s*[^\W_]+:) [^\n]*\s* )*
          \s*:$prop:[^\S\n]*
          "
        ) [^\n]*
        ( " )
      /$1$replacement$2/xg )
    {
         print "Found it!\n";
         print $str;
    }
    

    输出:

    Found it!
    
    env1:
      :prop1: "value1"
      :prop2: "value2"
            ...
      :propn: "valuen"
    
    env2:
      :prop1: "this changed"
            ...
      :prop2: "value2"
      :prop3: "value3"
      :propn: "valuen"
    
        ...
    
    envn:
      :prop1: "this changed"
            ...
      :prop3: "value3"
      :propn: "valuen"
    

    【讨论】:

    • 缺点:大RE!难以维护;需要参数调整才能像我提到的界面一样工作。 优点:选择不同环境的可能性。也是解决问题的。谢谢。
    【解决方案4】:

    Bash 脚本

    #!/bin/bash
    # tested with bash 4
    if [ $# -ne 4 ];then
        echo "Usage: .... "
        exit
    fi
    env=$1
    prop=$2
    text="$3"
    file=$4
    while read -r line
    do
        case "$line" in
            "$env"* )
            toggle=1
            ;;
        esac
        if [ "$toggle" = 1 ];then
            if [[ $line =~ "$prop" ]] ;then
                line="${line%%\"*}\"$text\""
                toggle=0
            fi
        fi
        echo "$line"
    done < $file > t
    mv t $file
    

    【讨论】:

    • 什么条件会取消设置切换变量?
    • 酷!首先,我认为在没有循环的情况下使用 RE,但这确实是一种方法。谢谢。
    【解决方案5】:

    此答案基于 Glenn Jackman 的 AWK 脚本,在我的测试中,由于输入 OP(以及您的真正)类型固有的缩进问题,该脚本失败。

    具体来说,检查我们是否在所需环境中的条件将发生在与检查我们是否具有所需属性不同的迭代中,因为它们通常位于不同的行上。因此,in_env &amp;&amp; $2 == prop 永远不会返回 true,考虑到 property : value 对将在单独的行上被读取为 $1 : $2

    此外,比较 $2 == prop 将受到前导空格的影响,需要修剪。我添加了几个不错的单行代码described here,以使脚本更易于阅读。

    最后,原始脚本在新值周围硬编码双引号,如果您要插入数值,这是一个问题。

    我以以下方式修改了脚本,这在我的测试用例中运行良好。我在这里提供以防对其他人有用。

    #!/bin/sh
    
    if [ $# -ne 4 ]; then
        echo "usage: $0 env prop value file" >&2
        exit 1
    fi
    
    awk -F : -v env="$1" -v prop="$2" -v val=" $3" '
        function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s }
        function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s }
        function trim(s) { return rtrim(ltrim(s)); }
        BEGIN {OFS = FS}
        $1 == env {in_env = 1}
        NF == 0   {in_env = 0}
        in_env && trim($1) == prop {$2 = val}
        {print}  
    ' "$4"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多