有没有办法将 jq 输出到 bash 脚本的多个变量中?
Is there a way to output jq into multiple variables for bash script?
基本上我有一个 bash 脚本,它在某一时刻发出 API 调用并生成证书和密钥并在 json 中返回。我将它通过管道传输到 jq 并且可以 select 证书或密钥并将其存储在变量中。
像这样:
CERT=$(API call | jq -r '.certificate')
or
KEY=$(API call | jq -r '.key')
我想将每个存储在它自己的变量中,但我不能调用两次,因为它会生成一个新的 cert/key。
我知道我可以将两者都存储在一个文件中,然后在完成我的任务后进行操作,但我很好奇 jq 是否提供了一种直接的方法来 select 将每个值主动存储在它自己的变量中?
您可以将原始 JSON 存储在变量(而不是文件)中,然后从中提取密钥和证书:
apiResult=$(API call)
cert=$(jq -r '.certificate' <<<"$apiResult")
key=$(jq -r '.key' <<<"$apiResult")
注意:我建议使用小写或混合大小写的变量,以避免与许多具有特殊含义的全大写名称发生意外冲突。此外,<<<
是一种 bashism,不适用于所有其他 shell;如果您需要它可以移植到例如破折号,使用类似 key=$(printf '%s\n' "$apiResult" | jq -r '.key')
.
如果两个值都不包含换行符,您可以轻松使用 read
:
$ read cert key < <( echo '{"certificate": "foo", "key": "bar"}' | jq -r .certificate,.key | tr \n ' ')
$ echo $cert
foo
$ echo $key
bar
或:
$ { read cert; read key; } << EOF
> $( echo '{"certificate": "foo", "key": "bar"}' | jq -r .certificate,.key )
> EOF
$ echo $cert:$key
foo:bar
证书几乎肯定会包含换行符,因此您可能希望序列化该数据(例如,对其进行 base64 编码)。但是您最好使用 Gordon Davisson 的方法。
除非字符串中有 NUL 字符,否则不需要多次调用 jq 或序列化数据。
在最简单的情况下,您可以按照以下几行进行:
{ IFS= read -r certificate
IFS= read -r key
echo "certificate=$certificate"
echo "key=$key"
} < <(API call | jq -r '.certificate, .key')
如果值不包含 NUL 但可能包含换行符,
那么你可以使用 NUL 作为分隔符。为了多样化,
我们还可以使用 while
循环:
while IFS= read -r -d $'[=11=]' certificate
do
IFS= read -r -d $'[=11=]' key
echo "certificate=$certificate"
echo "key=$key"
done < <(API call | jq -rj '[.certificate, .key] | join("\u0000")')
反之...
如果感兴趣的值可能包含文字 NUL 值(“\u0000”),那么这个问题似乎有问题,因为 bash 变量实际上不能包含文字 NUL。
如果任何感兴趣的值可能包含文字 NUL 值,那么这里有两种将“原始”字符串等价物提取到单独文件中的策略:
将 JSON 输出保存在一个(临时)文件中,并以显而易见的方式对每个感兴趣的值调用一次 jq -r
。
设置一个 bash 管道,开始于:
API 呼叫 | jq -r '.certificate, .key | @base64`
并继续一个循环,其中每一行都被解码,例如使用 base64 --decode
或 jq 的 @base64d
.
如果 API 调用生成非常大的 JSON 文档,则第二种策略可能有意义。
假设您创建了一个输出 shell 命令的程序。
$ data_source | jq -r '@sh "certificate=\( .certificate )\nkey=\( .key )"'
certificate='the certificate'
key='the key'
然后,您需要做的就是评估它们。
eval "$( data_source | jq -r '@sh "certificate=\( .certificate )\nkey=\( .key )"' )"
如果您要处理很多变量,您可以使用以下方法:
eval "$(
data_source |
jq -r '
{ "certificate": "certificate", "key": "key" } as $map |
( $map | keys_unsorted[] ) as $shell_var |
$shell_var + @sh "=\( .[$map[$shell_var]] )"
'
)"
您甚至可以使用路径。
eval "$(
data_source |
jq -r '
{ "certificate": [ "certificate" ], "key": [ "key" ] } as $map |
( $map | keys_unsorted[] ) as $shell_var |
$shell_var + @sh "=\( getpath($map[$shell_var]) )"
'
)"
基本上我有一个 bash 脚本,它在某一时刻发出 API 调用并生成证书和密钥并在 json 中返回。我将它通过管道传输到 jq 并且可以 select 证书或密钥并将其存储在变量中。
像这样:
CERT=$(API call | jq -r '.certificate')
or
KEY=$(API call | jq -r '.key')
我想将每个存储在它自己的变量中,但我不能调用两次,因为它会生成一个新的 cert/key。
我知道我可以将两者都存储在一个文件中,然后在完成我的任务后进行操作,但我很好奇 jq 是否提供了一种直接的方法来 select 将每个值主动存储在它自己的变量中?
您可以将原始 JSON 存储在变量(而不是文件)中,然后从中提取密钥和证书:
apiResult=$(API call)
cert=$(jq -r '.certificate' <<<"$apiResult")
key=$(jq -r '.key' <<<"$apiResult")
注意:我建议使用小写或混合大小写的变量,以避免与许多具有特殊含义的全大写名称发生意外冲突。此外,<<<
是一种 bashism,不适用于所有其他 shell;如果您需要它可以移植到例如破折号,使用类似 key=$(printf '%s\n' "$apiResult" | jq -r '.key')
.
如果两个值都不包含换行符,您可以轻松使用 read
:
$ read cert key < <( echo '{"certificate": "foo", "key": "bar"}' | jq -r .certificate,.key | tr \n ' ')
$ echo $cert
foo
$ echo $key
bar
或:
$ { read cert; read key; } << EOF
> $( echo '{"certificate": "foo", "key": "bar"}' | jq -r .certificate,.key )
> EOF
$ echo $cert:$key
foo:bar
证书几乎肯定会包含换行符,因此您可能希望序列化该数据(例如,对其进行 base64 编码)。但是您最好使用 Gordon Davisson 的方法。
除非字符串中有 NUL 字符,否则不需要多次调用 jq 或序列化数据。
在最简单的情况下,您可以按照以下几行进行:
{ IFS= read -r certificate
IFS= read -r key
echo "certificate=$certificate"
echo "key=$key"
} < <(API call | jq -r '.certificate, .key')
如果值不包含 NUL 但可能包含换行符,
那么你可以使用 NUL 作为分隔符。为了多样化,
我们还可以使用 while
循环:
while IFS= read -r -d $'[=11=]' certificate
do
IFS= read -r -d $'[=11=]' key
echo "certificate=$certificate"
echo "key=$key"
done < <(API call | jq -rj '[.certificate, .key] | join("\u0000")')
反之...
如果感兴趣的值可能包含文字 NUL 值(“\u0000”),那么这个问题似乎有问题,因为 bash 变量实际上不能包含文字 NUL。
如果任何感兴趣的值可能包含文字 NUL 值,那么这里有两种将“原始”字符串等价物提取到单独文件中的策略:
将 JSON 输出保存在一个(临时)文件中,并以显而易见的方式对每个感兴趣的值调用一次
jq -r
。设置一个 bash 管道,开始于:
API 呼叫 | jq -r '.certificate, .key | @base64`
并继续一个循环,其中每一行都被解码,例如使用
base64 --decode
或 jq 的@base64d
.
如果 API 调用生成非常大的 JSON 文档,则第二种策略可能有意义。
假设您创建了一个输出 shell 命令的程序。
$ data_source | jq -r '@sh "certificate=\( .certificate )\nkey=\( .key )"'
certificate='the certificate'
key='the key'
然后,您需要做的就是评估它们。
eval "$( data_source | jq -r '@sh "certificate=\( .certificate )\nkey=\( .key )"' )"
如果您要处理很多变量,您可以使用以下方法:
eval "$(
data_source |
jq -r '
{ "certificate": "certificate", "key": "key" } as $map |
( $map | keys_unsorted[] ) as $shell_var |
$shell_var + @sh "=\( .[$map[$shell_var]] )"
'
)"
您甚至可以使用路径。
eval "$(
data_source |
jq -r '
{ "certificate": [ "certificate" ], "key": [ "key" ] } as $map |
( $map | keys_unsorted[] ) as $shell_var |
$shell_var + @sh "=\( getpath($map[$shell_var]) )"
'
)"