Подтвердить что ты не робот

Sed: изменение значений свойств среды в файле .yml

У меня есть .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 script со следующим интерфейсом:

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

Я нашел этот пост, однако я не мог сделать это для моего дела. Заменить значение элемента XML? Регулярное выражение Sed?

Я также пробовал это: (он терпит неудачу, потому что изменяет все свойства)

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

Спасибо заранее! - Лоренко.

4b9b3361

Ответ 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"

изменить

Обратите внимание, что если вы ожидаете, что ввод содержит пробел в значениях, которые вы хотите изменить script, чтобы указать все переменные ( "$ 1", "$ 2"...). (Я сделал это сейчас, так как это лучшая практика с оболочкой).

/env/,/^[{space,tab}]*$/ - это адрес диапазона для sed. Он читает блок текста, содержащий настройки среды. Я предполагаю, что ваш пример ввода правильный и что каждый env отделен пустой строкой. Хм... это будет включать в себя последний файл.

** редактировать **

Благодаря @posdef для указания некоторых проблем с этим ответом. Код обновляется для решения конкретного случая.

Даже после исправления я заметил, что с учетом ввода типа

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

Соответствующий вывод был

     :prop2new value

Таким образом, без hardcoding дополнительных : и пробелов в подстановке это означает, что вам нужно быть очень подробным в том, как вы называете значение <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

Ответ 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"

Ответ 3

Этот ответ основан на Glenn Jackman AWK script, который в моем тестировании терпит неудачу из-за проблем с отступом, присущих типу ввода OP (и вашего по-настоящему).

В частности, условие проверки того, находимся ли мы в желаемой среде, будет происходить на другой итерации, чем проверка того, есть ли у нас желаемое свойство, поскольку они обычно будут на разных линиях. Таким образом, in_env && $2 == prop никогда не вернет true, считая, что пара property : value будет считана как $1 : $2 на отдельной строке.

Кроме того, сравнение $2 == prop будет страдать от ведущего пробела, который нужно обрезать. Я добавил пару приятных однострочных описанных здесь, чтобы сделать script более понятным для человека.

Наконец, исходные script жестко закодированные двойные кавычки вокруг нового значения, что является проблемой, если вы вставляете числовые значения.

Я изменил script следующим образом, который хорошо работает в моем тестовом примере. Я предоставляю здесь, если он полезен другим.

#!/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"

Ответ 4

Bash script

#!/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

Ответ 5

Вам понадобится хотя бы одно утверждение. Есть много способов сделать это, но вы должны использовать что-то, что умеет разбирать это.

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"