Разобрать вложенную переменную из файла YAML в bash

Хорошо помнить эти простые правила, и они применяются как к параметрам, так и к возвращаемым типам ...

  • Значение - копирует данный предмет.
  • Указатель - ссылается на адрес рассматриваемого элемента.
  • Ссылка - это буквально рассматриваемый предмет.

Есть время и место для каждого, поэтому убедитесь, что вы узнаете их. Локальные переменные, как вы показали здесь, просто таковы, что они ограничены временем, когда они локально живы в области функций. В вашем примере с возвращаемым типом int* и возвратом &i было бы одинаково некорректно. Вам было бы лучше в этом случае сделать это ...

void func1(int& oValue)
{
    oValue = 1;
}

Это приведет к прямому изменению значения вашего переданного параметра. В то время как этот код ...

void func1(int oValue)
{
    oValue = 1;
}

не будет. Он просто изменит значение oValue локально на вызов функции. Причина этого заключается в том, что вы фактически меняете только «локальную» копию oValue, а не oValue.

2
задан CodeMed 1 March 2019 в 00:57
поделиться

4 ответа

Если вы можете установить новые зависимости и планируете работать с большим количеством файлов yaml, yq - это оболочка для jq, которая может обрабатывать yaml. Это позволило бы безопасный (не grep) способ доступа к вложенным значениям yaml.

Использование выглядело бы примерно так: MY_VALUE=$(yq '.myValue.nested.value' < config-file.yaml)

В качестве альтернативы, Как я могу проанализировать файл YAML из сценария оболочки Linux? имеет парсер только для bash, который вы можете использовать для получения ваша ценность.

0
ответ дан willis 1 March 2019 в 00:57
поделиться

Поскольку другие комментируют, рекомендуется использовать yq (вместе с jq), если доступно.
Затем попробуйте следующее:

value=$(yq -r 'recurse | select(.name? == "CALICO_IPV4POOL_CIDR") | .value' "calico.yaml")
echo "$value"

Вывод:

192.168.0.0/16
0
ответ дан tshiono 1 March 2019 в 00:57
поделиться

Правильный способ сделать это - использовать язык сценариев и библиотеку синтаксического анализа YAML для извлечения интересующего вас поля.

Вот пример того, как это сделать в Python. Если бы вы делали это по-настоящему, вы бы, вероятно, разбили его на несколько функций и получили бы лучшие отчеты об ошибках. Это буквально просто для иллюстрации некоторых трудностей, вызванных форматом calico.yaml, который объединяет несколько документов YAML, а не один. Вам также нужно перебрать некоторые из внутренних списков в документе, чтобы извлечь интересующее вас поле.

#!/usr/bin/env python3

import yaml

def foo():
    with open('/tmp/calico.yaml', 'r') as fil:
        docs = yaml.safe_load_all(fil)
        doc = None
        for candidate in docs:
            if candidate["kind"] == "DaemonSet":
                doc = candidate
                break
        else:
            raise ValueError("no YAML document of kind DaemonSet")
        l1 = doc["spec"]
        l2 = l1["template"]
        l3 = l2["spec"]
        l4 = l3["containers"]
        for containers_item in l4:
            l5 = containers_item["env"]
            env = l5
            for entry in env:
                if entry["name"] == "CALICO_IPV4POOL_CIDR":
                    return entry["value"]
    raise ValueError("no CALICO_IPV4POOL_CIDR entry")

print(foo())

Однако иногда вам нужно решение прямо сейчас , и сценарии оболочки очень хороши в этом.

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

Что-то вроде следующего должно быть довольно устойчивым:

cat </tmp/calico.yaml | grep -A1 CALICO_IPV4POOL_CIDR | grep value: | cut -d: -f2 | tr -d ' "'

Хотя в конце стоит проверить с помощью регулярного выражения, что извлеченное значение действительно является действительной нотацией CIDR IPv4.

Ключевым моментом здесь является grep -A1 CALICO_IPV4POOL_CIDR.

Упомянутый вами двухэлементный словарь (показанный ниже) всегда будет отображаться как один фрагмент, так как это поддерево документа YAML.

    - name: CALICO_IPV4POOL_CIDR
      value: "192.168.0.0/16"

Ключи в calico.yaml не сортируются в алфавитном порядке в целом, но в конструкциях {"name": <something>, "value": <something else>}, name последовательно появляется до value.

0
ответ дан Gregory Nisbet 1 March 2019 в 00:57
поделиться

У вас есть две проблемы:

  • Как прочитать документ YAML из файла с несколькими документами
  • Как выбрать нужный ключ из этого документа YAML

Я догадался, что вам нужен документ YAML типа 'DaemonSet' из прочтения ответа Грегори Нисбетта.

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

Для библиотеки YAML я склонен использовать Ruby для этого, потому что:

  • Большинство систем имеют Ruby
  • Библиотека Ruby's Psych была включена в состав начиная с Ruby 1.9
  • ]
  • Библиотека PyYAML в Python немного негибкая и иногда ломается по сравнению с Ruby по моему опыту
  • Библиотека YAML в Perl часто не устанавливается по умолчанию

предложил использовать yq , но это не очень поможет в этом случае, потому что вам все еще нужен инструмент, который может извлечь документ YAML.

После извлечения документа я собираюсь снова использовать Ruby для сохранения файла в формате JSON. Тогда мы можем использовать JQ.

Извлечение документа YAML

Чтобы получить документ YAML с использованием Ruby и сохранить его как JSON:

url=...
curl -s $url | \
  ruby -ryaml -rjson -e \
    "puts YAML.load_stream(ARGF.read)
      .select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
  | jq . > calico.json

Дальнейшее объяснение:

  • YAML.load_stream читает документы YAML и возвращает их как массив
  • ARGF.read читает из файла, переданного через STDIN
  • Выбор Ruby позволяет легко выбирать документ YAML в соответствии с к его доброму ключу
  • Затем мы берем элемент 4 и преобразуем в JSON.

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

Выбор нужной клавиши

Чтобы выбрать нужную клавишу, можно использовать следующий JQ-запрос:

jq -r \
  '.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
  calico.json                                                          

Дальнейшее объяснение:

[ 1140]
  • Первая часть spec.template.spec.containers[].env[] выполняет итерации для всех контейнеров и для всех envs внутри них
  • Затем мы выбираем хеш, где ключ имени равен CALICO_IPV4POOL_CIDR, и возвращаем значение
  • The - r удаляет кавычки вокруг строки
    • Собирая все вместе:

      #!/usr/bin/env bash
      
      url='https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml'
      
      curl -s $url | \
        ruby -ryaml -rjson -e \
          "puts YAML.load_stream(ARGF.read)
            .select{|doc| doc['kind']=='DaemonSet'}[0].to_json" \
        | jq . > calico.json
      
      jq -r \
        '.spec.template.spec.containers[].env[] | select(.name=="CALICO_IPV4POOL_CIDR") | .value' \
        calico.json
      

      Тестирование:

      ▶ bash test.sh
      192.168.0.0/16
      
    0
    ответ дан Alex Harvey 1 March 2019 в 00:57
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: