问题
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:
-R
reads raw text, rather than trying to parse the input as JSON-n
preventsjq
from reading input itself.inputs
reads all the input explicitly, allowing an array of names to be built.map
creates an array of objects withkey
andvalue
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.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