'exporting environment variables with spaces using jq

So, I'm trying to export an environment variable that comes from an api that returns json values. Would like to use jq to just do a one liner, but if the values have spaces I cannot get it working

Trying without surrounding the value in quotes

/app/src $ $(echo '{"params":[{ "Name":"KEY","Value":"value with space"}]}' | jq
 -r '.params[] | "export " + .Name + "=" + .Value')
/app/src $ printenv KEY
value
/app/src $ 

Next, I try wrapping the value in quotes

/app/src $ $(echo '{"params":[{ "Name":"KEY","Value":"value with space"}]}' | jq
 -r '.params[] | "export " + .Name + "=\"" + .Value + "\""')
sh: export: space": bad variable name
/app/src $ 


Solution 1:[1]

For all of the below, I'm assuming that:

json='{"params":[{ "Name":"KEY","Value":"value with space"}]}'

It can be done, but ONLY IF YOU TRUST YOUR INPUT.

A solution that uses eval might look like:

eval "$(jq -r '.params[] | "export \(.Name | @sh)=\(.Value | @sh)"' <<<"$json")"

The @sh builtin in jq escapes content to be eval-safe in bash, and the eval invocation then ensures that the content goes through all parsing stages (so literal quotes in the data emitted by jq become syntactic).

However, all solutions that allow arbitrary shell variables to be assigned have innate security problems, as the ability to set variables like PATH, LD_LIBRARY_PATH, LD_PRELOAD and the like can be leveraged into arbitrary code execution.


Better form is to generate a NUL-delimited key/value list...

build_kv_nsv() {
  jq -j '.params[] |
    ((.Name | gsub("\u0000"; "")),
     "\u0000",
     (.Value | gsub("\u0000"; "")),
     "\u0000")'
}

...and either populate an associative array...

declare -A content_received=( )
while IFS= read -r -d '' name && IFS= read -r -d '' value; do
  content_received[$name]=$value
done < <(build_kv_nsv <<<"$json")

# print the value of the populated associative array
declare -p content_received

...or to use a namespace that's prefixed to guarantee safety.

while IFS= read -r -d '' name && IFS= read -r -d '' value; do
  printf -v "received_$name" %s "$value" && export "received_$name"
done < <(build_kv_nsv <<<"$json")

# print names and values of our variables that start with received_
declare -p "${!received_@}" >&2

Solution 2:[2]

If the values are known not to contain (raw) newlines, and if you have access to mapfile, it may be worthwhile considering using it, e.g.

$ json='{"params":[{ "Name":"KEY","Value":"value with space"}]}'
$ mapfile -t KEY < <( jq -r '.params[] | .Value' <<< "$json" )
$ echo N=${#KEY[@]}
N=1

If the values might contain (raw) newlines, then you'd need a version of mapfile with the -d option, which could be used as illustrated below:

$ json='{"params":[{ "Name":"KEY1","Value":"value with space"}, { "Name":"KEY2","Value":"value with \n newline"}]}'
$ mapfile -d $'\0' KEY < <( jq -r -j '.params[] | .Value + "\u0000"' <<< "$json" )
$ echo N=${#KEY[@]}
N=2

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 peak