Generating a JSON map containing shell variables named in a list

有些话、适合烂在心里 提交于 2020-02-04 06:33:29

问题


My shell-fu is at a below-beginner level. I have a file that contains some lines that happen to be the names of environment variables.

e.g.

ENV_VAR_A
ENV_VAR_B
...

What I want to do is use this file to generate a JSON string containing the names and current values of the named variables using jq like this:

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

I'm trying to create the jq command through a shell script and I have no idea what I'm doing :(


回答1:


Short and sweet (if you have jq 1.5 or higher):

 jq -Rn '[inputs | {(.): env[.]}] | add' tmp.txt



回答2:


What you want here is an indirect reference. Those can be done with ${!varname}. As a trivial example limited to exactly two lines:

# 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}'

To support an arbitrary number of key/value pairs, consider instead something like:

# 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
  '



回答3:


jq can look up the values from the environment itself.

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

A few notes on how this works:

  1. -R reads raw text, rather than trying to parse the input as JSON
  2. -n prevents jq from reading input itself.
  3. inputs reads all the input explicitly, allowing an array of names to be built.
  4. map creates an array of objects with key and value as the keys; . is the current array input (a variable name), and $ENV[.] is the value of the environment variable whose name is the current array input.
  5. from_entries finally coalesces all those {"key": ..., "value": ...} objects into a single object.



回答4:


Try something along the following script in 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}"

Just something that hopefully will give you a easier start with your journey in bash.

I guess I would do something unreadable with xargs like:

< 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,
)}"


来源:https://stackoverflow.com/questions/59778578/generating-a-json-map-containing-shell-variables-named-in-a-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!