sed: изменить значения свойств окружения в файле .yml - PullRequest
12 голосов
/ 20 апреля 2011

У меня есть файл .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"

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

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

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

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

Ответы [ 5 ]

4 голосов
/ 20 апреля 2011

(очень хороший первый пост!)

Попробуйте это

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" ...). (Я сделал это сейчас, так как это лучший метод сценариев оболочки).

/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

3 голосов
/ 20 апреля 2011

Я бы использовал 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 голосов
/ 20 сентября 2016

Этот ответ основан на сценарии AWK Гленна Джекмана, который в моем тестировании не удался из-за проблем с отступами, присущих типу ввода OP (и ваш действительно) имеет.

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

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

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

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

#!/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"
1 голос
/ 20 апреля 2011

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
0 голосов
/ 20 апреля 2011

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

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...