LINUX.ORG.RU

Лучший способ загрузки key-value из yaml и передача их в bash функцию

 , , ,


0

1

Есть отдельная тема по аргументам. А эта тема отдельная. Напрямую связанная с yq и передача данных в баш функцию так, чтобы было удобно с этим всем работать. Зачем? Разные функции будут обрабатывать разный «dataset». Количество данных разное и объемное. Вручную создавать переменные внутри функции - не очень практично. Лучше это сделать автоматизированно

cat ./example1

- some1: "value1"
  some2: val2
  some3: val3
source_data() {
    local path="${1}"
    shift 1

cat<<EOF
$(cat "${path}")
EOF
}

f1() {
    # формируем список локальных переменных, имена которых являются
    # именами ключей передаваемого "dataset" с помощью цикла
    # т.е происходит иммитация
    # local some1="value1"
    # local some2=val2
    # local some3=val3
    # чтобы можно было работать с аргументами по именам
    echo
}

parse() {
    local target="${1}"
    shift 1

    if [[ ! -f "${target}" ]]; then
        printf "${COLOR_RED}Error: cannot load ${target}\n"
    fi

    local data=$(printf "%s %s" source_data ${target})

    local i=0
    while ${data} | yq ".[$i]" -e > /dev/null 2>&1; do
        local x="$(${data} | yq ".[$i]")"
        echo ${x}
        # f1(x) # <-- передаем "dataset" в функцию
        ((i++))
    done
}

parse "./example1.yml"
★★★★

Последнее исправление: serg002 (всего исправлений: 1)

Пишешь конвертор yaml в ini: «key: value» -> «[local] key=value» и source’ишь. Делай сам, так как ты явно пишешь что-то «серьезное» на баше, не зная баш. Старайся не городить конструкции вида, это похуже чем eval

local data=$(printf ...)
while ${data} ...
anonymous
()

Любой карпиз за ваши деньги Сергей

$ cat example.yaml
- some1: "value1"
  some2: val2
  some3: val3
$ /snap/bin/yq eval '.. | select((tag == "!!map" or tag == "!!seq") | not) | (path 
| join("_")) + "=" + .' example.yaml | awk '!/=$/{print }'
0_some1=value1
0_some2=val2
0_some3=val3

@ugoday как бы такое преобразование выглядело на барабашке, где на вход ей подается example.yaml, а на выходе барабашка должна список bash переменных?

gagarin0
()
Последнее исправление: gagarin0 (всего исправлений: 1)

У меня вопрос: а нафига это писать на баше?

На выходе портянка, которая для понимания требует напрягать мозги, отвечая на 100500 вопрос «а что тут делается». Вызов 100500 вспомогательных утилит, каждая из которых будет отдельным процессом в ОС. Странное использование yaml с обрезанием его функционала. И т.д. и т.п.

При этом в системе есть python, на котором это пишется в разы легче. Код понятнее. Функционала больше.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

При этом в системе есть python, на котором это пишется в разы легче. Код понятнее. Функционала больше.

И зависимость в виде python. А мне нужно без зависимостей

serg002 ★★★★
() автор топика
Ответ на: комментарий от serg002

Какая это зависимость? Она ничем не отличается от зависимости bash. И уж тем более от зависимости yq, которая уж очень популярна в типовой установке.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

И зависимость в виде python. А мне нужно без зависимостей

Ну то есть, поставить python + pyyaml - это зависимости?

А поставить yq, который потянет python + pyyaml - это не зависимость?

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Упс

читайте внимательно:

Лучший способ загрузки key-value из yaml и передача их в bash функцию (комментарий)

$ /snap/bin/yq --version
yq (https://github.com/mikefarah/yq/) version v4.44.5
gagarin0
()
Последнее исправление: gagarin0 (всего исправлений: 1)

Что-то я на старого ворчуна похож…

Про bash/python просто моё мнение: каждый инструмент хорош в своём. Я да же не вижу проблемы написать несколько скриптов и на bash и на python одновременно. Если каждый скрипт будет простым и читабельным для любого junior-а.

AlexVR ★★★★★
()
Ответ на: комментарий от gagarin0

Да всё как обычно: читаем файл и получаем нужную структуру данных: список словарей.

user> (def data (-> "/tmp/tmp.ybEU7QYXdM/example.yaml"
     slurp
     (yaml/parse-string {:keywords false})))
#'user/data
user> data
({"some1" "value1", "some2" "val2", "some3" "val3"}
 {"some1" "value1", "some2" "val2", "some3" "val3"})

Вот так мы можем превратить словарь в список bash переменных.

user> (defn print-pairs [i key-value-map]
  (doseq [[k v] key-value-map]
    (printf "%s_%s=%s\n" i k v)))
#'user/print-pairs
user> (print-pairs 0 (first data))
0_some1=value1
0_some2=val2
0_some3=val3

проходим в цикле по списку и печатаем каждый словарь. Всё вместе

(defn gen-variables [kv-sequence]
  (loop [i 0
         [x & xs :as kv] kv-sequence]
    (when (not-empty kv)
      (print-pairs i x) 
      (recur (+ i 1) xs))))
user> (gen-variables data)
0_some1=value1
0_some2=val2
0_some3=val3
1_some1=value1
1_some2=val2
1_some3=val3
nil

Дополнительная возможность. Если мы вместо разделённых символом перевода строки пар ключ=значение будем использовать переменные окружения. Тогда нам вообще не нужно гонять строки туда-сюда. С помощью babashka/process можно сразу указать словарь при запуске нового процесса. Т.е.

user> (-> (proc/shell {:out :string :env (first data)} "env") 
          :out 
          println)
some2=val2
some1=value1
some3=val3
ugoday ★★★★★
()

передача данных в баш функцию так, чтобы было удобно с этим всем работать.

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

ugoday ★★★★★
()
Ответ на: комментарий от gagarin0

Существует странный психологический феномен, благодаря которому непривычных к лиспу людей )))) вгоняет в ужас, а

      )
    }
  }
}

вообще не напрягают. Отчего так — не ведаю.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Отчего так — не ведаю.

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

По-теме:

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

MoldAndLimeHoney
()
Ответ на: комментарий от AlexVR

Если каждый скрипт будет простым и читабельным для любого junior-а.

ТС что-то молчит.

Выделил минутку:

yaml_input.yml:

- foo: "value1"
  bar: 12
- foo: "value2"
  bar: 21
  baz: 21

yaml_task.sh:

#!/bin/sh

echo "Args: $@"
echo "YAML_FOO=${YAML_FOO}"

yaml_parse.py:

#!/bin/env python3
import yaml
import subprocess


def run_task(task_args):
    prog = ['./yaml_task.sh']
    env = {}
    for arg, value in task_args.items():
        value = str(value)
        match arg:
            case 'foo':
                env['YAML_FOO'] = value
            case _:
                prog += ['--'+arg, value]
    subprocess.run(prog, env=env)


def main():
    with open('yaml_input.yml', 'r') as file:
        data = yaml.load(file, yaml.Loader)
    for task_args in data:
        run_task(task_args)


if __name__ == '__main__':
    main()

Результат:

$ ./yaml_parse.py
Args: --bar 12
YAML_FOO=value1
Args: --bar 21 --baz 21
YAML_FOO=value2
AlexVR ★★★★★
()
Ответ на: комментарий от serg002

А мне нужно без зависимостей

Ну если без зависимостей. То можно не использовать Yaml, а писать прямо на bash:

Например, вместо FOO.yml:

- foo: "value1"
  bar: 12
- foo: "value2"
  bar: 12
  baz: 21
- foo: "value2"
  bar: 12

Пишем:

target_foo() {
    run_task --foo value1 --bar 12
    run_task --foo value2 --bar 12 --baz 21
    run_task --foo value3 --bar 42
}

Полный пример

#!/bin/bash

run_task() {
    local foo bar baz
    while [[ "$#" -gt 0 ]]
    do
        case "$1" in
            --foo)
                foo="$2"
                ;;
            --bar)
                bar="$2"
                ;;
            --baz)
                baz="$2"
                ;;
            *)
                echo "Invalid argument: $1"
                exit 1
        esac
        shift
        shift
    done

    echo "foo: ${foo}"
    echo "bar: ${bar}"
    if [[ -n "$baz" ]]; then
        echo "baz: ${baz}"
    fi

    echo "TODO..."
    echo ""
}

target_foo() {
    run_task --foo value1 --bar 12
    run_task --foo value2 --bar 12 --baz 21
    run_task --foo value3 --bar 42
}

target() {
    case "$1" in
        FOO)
            target_foo
            ;;
        *)
            echo "Unknown target"
            exit 1
    esac
}

target FOO
AlexVR ★★★★★
()