sed:更改.yml文件中环境属性的值

时间:2011-04-19 22:24:08

标签: regex parsing bash sed

我有一个配置应用程序环境属性的.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"/'

提前致谢! - Lourenco。

5 个答案:

答案 0 :(得分:4)

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

试试这个

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

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

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

相关输出

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

IHTH

答案 1 :(得分:3)

我使用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"

答案 2 :(得分:2)

这个答案是基于Glenn Jackman的AWK脚本,在我的测试中由于输入类型OP(以及你真正的)所固有的缩进问题而失败了。

具体地,检查我们是否处于期望环境中的条件将发生在与检查我们是否具有期望属性不同的迭代上,因为这些通常在不同的行上。因此in_env && $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"

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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"