Генерация карты JSON, содержащей переменные оболочки, названные в списке - PullRequest
2 голосов
/ 17 января 2020

Мой shell-fu находится на уровне ниже начального. У меня есть файл, который содержит несколько строк, которые являются именами переменных среды.

например

ENV_VAR_A
ENV_VAR_B
...

Что я хочу сделать, это использовать этот файл для генерации строки JSON содержащий имена и текущие значения именованных переменных с использованием jq, например:

jq -n --arg arg1 "$ENV_VAR_A" --arg arg2 "$ENV_VAR_B" '{ENV_VAR_A:$arg1,ENV_VAR_B:$arg2}'

# if ENV_VAR_A=one and ENV_VAR_B=two then the preceding command would output 
# {"ENV_VAR_A":"one","ENV_VAR_B":"two"}

Я пытаюсь создать команду jq с помощью сценария оболочки, и я понятия не имею, что я делаю: (

Ответы [ 4 ]

5 голосов
/ 17 января 2020

Короткие и сладкие (если у вас jq 1,5 или выше):

 jq -Rn '[inputs | {(.): env[.]}] | add' tmp.txt
2 голосов
/ 17 января 2020

jq может искать значения из самой среды.

$ export A=1
$ export B=2
$ cat tmp.txt
A
B
$ jq -Rn '[inputs] | map({key: ., value: $ENV[.]}) | from_entries' tmp.txt
{
  "A": "1",
  "B": "2"
}

Несколько замечаний о том, как это работает:

  1. -R читает необработанный текст, а чем попытка проанализировать входные данные как JSON
  2. -n препятствует jq самому читать входные данные.
  3. inputs читает все входные данные явно, позволяя массиву имен быть встроенный.
  4. map создает массив объектов с ключами key и value; . - это текущий вход массива (имя переменной), а $ENV[.] - значение переменной среды, имя которой - текущий вход массива.
  5. from_entries, наконец, объединяет все эти {"key": ..., "value": ...} объекты в один объект.
2 голосов
/ 17 января 2020

Здесь вам нужна косвенная ссылка . Это можно сделать с помощью ${!varname}. В качестве тривиального примера, ограниченного ровно двумя строками:

# read arg1_varname and arg2_varname from the first two lines of file.txt
{ read -r arg1_varname; read -r arg2_varname; } <file.txt

# pass the variable named by the contents of arg1_varname as $arg1 in jq
# and the variable named by the contents of arg2_varname as $arg2 in jq
jq -n --arg arg1_name "$arg1_varname" --arg arg1_value "${!arg1_varname}" \
      --arg arg2_name "$arg2_varname" --arg arg2_value "${!arg2_varname}" \
  '{($arg1_name):$arg1_value, ($arg2_name):$arg2_value}'

Для поддержки произвольного числа пар ключ / значение рассмотрим вместо этого что-то вроде:

# Transform into NUL-separate key=value pairs (same format as /proc/*/environ)
while IFS= read -r name; do                             # for each variable named in file.txt
  printf '%s=%s\0' "$name" "${!name}"                   # print its name and value, and a NUL
done \
  <file.txt \
  | jq -Rs 'split("\u0000")                             # split on those NULs
            | [.[] | select(.)                          # ignore any empty strings
               | capture("^(?<name>[^=]+)=(?<val>.*)$") # break into k/v pairs
               | {(.name): .val}]                       # make each a JSON map
            | add                                       # combine those maps
  '
1 голос
/ 17 января 2020

Попробуйте что-нибудь по следующему сценарию в bash:

# array of arguments to pass to jq
jqarg=()
# the script to pass to jq
jqscript=""
# just a number for the arg$num for indexing
# suggestion: just index using variable names...
num=1

# for each variable name from the input
while IFS= read -r varname; do

   # just an assertion - check if the variable is not empty
   # the syntax ${!var} is indirect reference
   # you could do more here, ex. see if such variable exists
   # or if $varname is a valid variable name
   if [[ -z "${!varname}" ]]; then
        echo "ERROR: variable $varname has empty value!" >&2
        exit 50
   fi

   # add the arguments to jqarg array
   jqarg+=(--arg "arg$num" "${!varname}")
   # update jqscript
   # if jqscript is not empty, add a comma on the end
   if [[ -n "$jqscript" ]]; then
      jqscript+=","
   fi
   # add the ENV_VAR_A:$arg<number>
   jqscript+="$varname:\$arg$num"
   # update number - one up!
   num=$((num + 1))

# the syntax of while read loop is that input file is on the end
done < input_file_with_variable_names.txt

# finally execute jq
# note the `{` and `}` in `{$jqscript}` are concious
jq -n "${jqarg[@]}" "{$jqscript}"

Просто то, что, как мы надеемся, облегчит вам начало вашего путешествия в bash.

Я думаю I сделает что-то нечитаемое с xargs, например:

< input_file_with_variable_names.txt xargs -d$'\n' -n1 bash -c '
   printf %s\\0%s\\0%s\\0 --arg "$1" "${!1}"
' -- |
xargs -0 sh -c 'jq -n "$@" "$0"' "{$(
     sed 's/\(.*\)/\1: $\1 /' input_file_with_variable_names.txt | 
     paste -sd,
)}"
...