Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sed: change values of properties of an environment in a .yml file

I have an .yml file that configures environment properties of an application, such like this:

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

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

    ...

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

I would like to produce a bash script with the following interface:

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

Example:

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

The output will be:

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"

I found this post, however I could not do it work for my case. Replace an XML element's value? Sed regular expression?

I also tried this: (it fails because alters all properties)

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

Thanks in advance! -- Lourenco.

like image 657
Lourenco Avatar asked Apr 19 '11 22:04

Lourenco


2 Answers

(very nice first post!)

Try this

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"

edit

Note, if you expect input to contain white-space in the values you'll want to modify script to quote all variables ("$1","$2"...). (I have now done this, as it is a shell-scripting best practice).

The /env/,/^[{space,tab}]*$/ is a range address for sed. It reads a block of text that contains your environment settings. I'm assuming your sample input is correct and that each env is separated by a blank line. Hmm... this would include the last one in the file.

** edit**

Thanks to @posdef for pointing some problems with this answer. The code is updated to solve the particular case.

Even after the fix, I did notice that given an input like

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

The relevant output was

     :prop2new value

So, without hardcoding extra : and space chars into the substitution, this means you'll need to be very verbose in how you call the <property> value AND the <new value> , i.e.

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

relevant output

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

IHTH

like image 85
shellter Avatar answered Oct 21 '22 06:10

shellter


I'd use 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"
like image 36
glenn jackman Avatar answered Oct 21 '22 07:10

glenn jackman