在 jq 中,我如何将 JSON 转换为 key=value
形式的字符串?
来自:
{
"var": 1,
"foo": "bar",
"x": "test"
}
收件人:
var=1
foo=bar
x=test
你可以尝试:
jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' test.json
这是一个演示:
$ cat test.json
{
"var": 1,
"foo": "bar",
"x": "test"
}
$ jq -r 'to_entries|map("\(.key)=\(.value|tostring)")|.[]' test.json
foo=bar
var=1
x=test
# Denote the input of recursively_reduce(f) by $in.
# Let f be a filter such that for any object o, (o|f) is an array.
# If $in is an object, then return $in|f;
# if $in is a scalar, then return [];
# otherwise, collect the results of applying recursively_reduce(f)
# to each item in $in.
def recursively_reduce(f):
if type == "object" then f
elif type == "array" then map( recursively_reduce(f) ) | add
else []
end;
def kv: to_entries | map("\(.key)=\(.value)");
[ {"a":1}, [[{"b":2, "c": 3}]] ] | recursively_reduce(kv)
#=> ["a=1","b=2","c=3"]
更新:在jq 1.5发布后,walk/1被添加为jq定义的内置函数。它可以与上述定义的kv一起使用,例如:
walk(if type == "object" then kv else . end)
使用以上输入,结果将是:
[["a=1"],[[["b=2","c=3"]]]]
为了“展开”输出,可以使用flatten/0。以下是一个完整的示例:
jq -cr 'def kv: to_entries | map("\(.key)=\(.value)");
walk(if type == "object" then kv else . end) | flatten[]'
输入:
[ {"a":1}, [[{"b":2, "c": 3}]] ]
输出:
a=1
b=2
c=3
顺便提一下,基于@aioobe的出色回答。如果您需要将键全部大写,可以使用ascii_upcase
通过修改他的示例来实现:
jq -r 'to_entries|map("\(.key|ascii_upcase)=\(.value|tostring)")|.[]'
我有一个与您类似的情况,但是在创建用于访问AWS的环境变量时想要大写所有键。
$ okta-credential_process arn:aws:iam::1234567890123:role/myRole | \
jq -r 'to_entries|map("\(.key|ascii_upcase)=\(.value|tostring)")|.[]'
EXPIRATION=2019-08-30T16:46:55.307014Z
VERSION=1
SESSIONTOKEN=ABcdEFghIJ....
ACCESSKEYID=ABCDEFGHIJ.....
SECRETACCESSKEY=AbCdEfGhI.....
LD_LIBRARY_PATH
、PATH
和LD_PRELOAD
这样安全敏感的东西。你应该确保不受信任的变量具有小写名称,以防止它们破坏你的操作系统。请参阅https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html(“包含小写字母的环境变量名命名空间专用于应用程序”)。 - Charles Duffy没有使用jq
,我可以使用grep
和 sed
导出json中的每个条目,但这仅适用于简单情况,即我们只有键/值对。
for keyval in $(grep -E '": [^\{]' fileName.json | sed -e 's/: /=/' -e "s/\(\,\)$//"); do
echo "$keyval"
done
这里是一个示例响应:
❯ for keyval in $(grep -E '": [^\{]' config.dev.json | sed -e 's/: /=/' -e "s/\(\,\)$//"); do
echo "$keyval"
done
"env"="dev"
"memory"=128
"role"=""
"region"="us-east-1"
to_entries[]|join("=")
$ echo '{"var": 1, "foo": "bar", "x": "test"}' | \
jq -r 'to_entries[]|join("=")'
var=1
foo=bar
x=test
null
的安全性使用问号jsn_
前缀对生成的变量进行命名空间处理,以避免覆盖您的普通变量$ cat nil.jsn
null
$ jq -r 'to_entries? | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <nil.jsn
$ cat test.jsn
{
"var": 1,
"foo": [
"bar",
"baz",
"space test"
],
"x": "test"
}
$ jq -r 'to_entries? | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <nil.jsn
$ jq -r 'to_entries? | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <test.jsn
set -A jsn_'var' -- 1
set -A jsn_'foo' -- 'bar' 'baz' 'space test'
set -A jsn_'x' -- 'test'
$ jq -r 'to_entries? | map("jsn_\(.key | @sh)=(\(.value | @sh))") | .[]' <nil.jsn
$ jq -r 'to_entries? | map("jsn_\(.key | @sh)=(\(.value | @sh))") | .[]' <test.jsn
jsn_'var'=(1)
jsn_'foo'=('bar' 'baz' 'space test')
jsn_'x'=('test')
在所有后续的示例中,我将使用 Korn Shell 变体,但对上述两个变体之间的差异应用将适用于它们。
在这两种情况下,您都会得到数组,但对数组本身进行解引用与解引用其元素 #0 是相同的,因此即使对于单个值也是安全的:
$ eval "$(jq -r 'to_entries? | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <test.jsn)"
$ echo $jsn_x
test
$ echo $jsn_foo = ${jsn_foo[0]} but not ${jsn_foo[1]}
bar = bar but not baz
然而,它也有一个缺点:仅为sh
编写的脚本不支持数组,因此您需要选择bash
、ksh88
、ksh93
、mksh
、zsh
(可能还有其他选项),或者它们的共同子集(所有这些都支持[[ … ]]
;除了ksh88
之外,其他都应该支持GNU bash风格的数组)。
null
(见下文)$ jq -r 'if . == null then
> null
> else
> to_entries | map(
> select(IN(.key;
> "foo", "val", "var"
> )) | "set -A jsn_\(.key | @sh) -- \(.value | @sh)"
> ) | .[]
> end' <nil.jsn
null
$ jq -r 'if . == null then
> null
> else
> to_entries | map(
> select(IN(.key;
> "foo", "val", "var"
> )) | "set -A jsn_\(.key | @sh) -- \(.value | @sh)"
> ) | .[]
> end' <test.jsn
set -A jsn_'var' -- 1
set -A jsn_'foo' -- 'bar' 'baz' 'space test'
foo
、val
(此处不存在)和 var
是允许的。这可以用来过滤掉值不是简单值或一维简单值的JSONArray的键,以确保结果是安全的¹。set -o pipefail
if ! vars=$(curl "${curlopts[@]}" "$url" | jq -r '
if . == null then
null
else
to_entries | map(
select(IN(.key;
"foo", "val", "var"
)) | "set -A jsn_\(.key | @sh) -- \(.value | @sh)"
) | .[]
end
'); then
echo >&2 "E: no API response"
exit 1
fi
if [[ $vars = null ]]; then
echo >&2 "E: empty API response"
exit 1
fi
eval "$vars"
echo "I: API response: var=$jsn_var"
for x in "${jsn_foo[@]}"; do
echo "N: got foo '$x'"
done
jq
时,如果不使用问号,它会抛出错误,但是如果确实存在多维数组或其他诡计的话,失败模式就不太友好了。$ cat test.jsn
{
"foo": [
[
"bar",
"baz"
],
"space test"
],
"var": 1,
"x": "test"
}
$ jq -r 'to_entries? | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <test.jsn
$ echo $?
0
$ jq -r 'to_entries | map("set -A jsn_\(.key|@sh) -- \(.value|@sh)") | .[]' <test.jsn
jq: error (at <stdin>:11): array (["bar","baz"]) can not be escaped for shell
$ echo $?
5
$ jq -r 'if . == null then
> null
> else
> to_entries | map(
> select(IN(.key;
> "foo", "val", "var"
> )) | "set -A jsn_\(.key | @sh) -- \(.value | @sh)"
> ) | .[]
> end' <test.jsn
jq: error (at <stdin>:11): array (["bar","baz"]) can not be escaped for shell
$ echo $?
5
jsn_'var'=(1)
这样将引号放在左侧的赋值方式。在允许输出之前,可能需要根据POSIX规则检查变量名(首字符为字母,剩余字符可以是字母、数字或下划线)。 - Charles Duffykey=val<NUL>
序列。在jq中,NUL可以表示为"\u0000"
;使用jq -j
可以抑制自动换行,因此您可以在项目之间添加自己的分隔符,然后使用类似于(在bash中)declare -A vars=(); while IFS= read -r -d '' item; do [[ $item = *=* ]] || continue; vars[${item%%=*}]=${item#*=}; done < <(jq ...)
的循环来填充一个数组。(当然,为了保持谨慎,我们需要确保键和值都不包含NUL,并且键不包含=
;因此仍然需要进行一些验证)。 - Charles Duffy<(jq…)
的bashism)。 - mirabilos
\(.key | tr a-z A-Z)
但是没有成功。 - Dane Jordanjq -r 'to_entries|map("\(.key|@uri)=\(.value|@uri)")|join("&")'
可以得到一个有效的查询字符串。 - cachiusjq -r 'to_entries|map("\(.key)=" + @sh "\(.value|tostring)")|.[]'
。自jq
版本1.3以来,@sh
字符串格式化程序可用。 - ack