如何使用 jq 中的键和值生成 bash 关联数组?
How can I use keys and values from jq to generate a bash associative array?
来自 Json 结构,我想要一个特定的字典。从键的颜色黄色或红色,我添加 id 值。
[
{
"id": "9b058640",
"type": "db",
"color": "red",
"host": "db1"
},
{
"id": "0u858640",
"type": "db",
"color": "yellow",
"host": "db2"
},
{
"id": "0ui9k40",
"type": "net",
"color": "red",
"host": "net1"
},
{
"id": "5ty87a",
"type": "net",
"color": "yellow",
"host": "net2"
}
]
所以我想获取 X 字典
X=(
['yellow']="9b058640 5ty87a"
['red']="9b058640 0ui9k40"
)
我可以按值解析 :
jq -c '.[] | select(.color | contains("red"))'
您可以在 jq 中使用 @tsv
运算符来发出 tab-separated 输出,bash while read
循环可以轻松地将其解析为输入。
假设您的输入 JSON 在变量 s
中:
declare -A X=( )
while IFS=$'\t' read -r color id; do
X[$color]+="$id "
done < <(jq -r '.[] | [.color, .id] | @tsv' <<<"$s")
上面确实走了一个小捷径,因为它在每个项目之后留下了尾随 space。如果出于某种原因这是不可接受的,您可以随时遍历数组并在事后使用第二个循环清理它:
for color in "${!X[@]}"; do
X[$color]=${X[$color]%" "}
done
您可以在 https://replit.com/@CharlesDuffy2/IndigoRemoteEngineering
的沙箱中看到此 运行
或者,使用 eval
:
#!/usr/bin/env bash
case $BASH_VERSION in ''|[0-3].*) echo "ERROR: bash 4.0+ required" >&2; exit 1;; esac
declare -A X=( )
eval "$(
jq -r '
reduce .[] as $item ({}; .[$item.color] += [$item.id])
| to_entries[]
| "X[\(.key | @sh)]=\(.value | join(" ") | @sh)"
'
)" <file.json
答案:
declare -A a
. <(jq -r '"a[\(.color)]+=${a[\(.color)]+ }\(.id)"' file.json)
提供:
$ declare -p a
$ declare -A a=([red]="9b058640 0ui9k40" [yellow]="0u858640 5ty87a" )
# or
$ echo "${a[red]}"; echo "${a[yellow]}"
9b058640 0ui9k40
0u858640 5ty87a
解释:
- 在
jq
中构造以下bash代码:
a[color]+=${a[color]+ }id
${a[color]+ }
扩展为
,仅当 a[color]
为空时。
"\(.id)"
是 jq 字符串插值 - 它被替换为 .id
的值
- 使用
.
和一个进程子来获取此代码
不需要 .
、source
、eval
甚至 bash 中的循环。您只需要 declare
和 jq
,它们可以使用 escaping with @sh
and string interpolation:
构造声明
declare -A X="($(
jq -r '
("yellow", "red") as $color
| @sh "[\($color)]=\(map(select(.color == $color).id) | join(" "))"
' input.json
))"
$ echo "${X[yellow]}"
0u858640 5ty87a
$ echo "${X[red]}"
9b058640 0ui9k40
来自 Json 结构,我想要一个特定的字典。从键的颜色黄色或红色,我添加 id 值。
[
{
"id": "9b058640",
"type": "db",
"color": "red",
"host": "db1"
},
{
"id": "0u858640",
"type": "db",
"color": "yellow",
"host": "db2"
},
{
"id": "0ui9k40",
"type": "net",
"color": "red",
"host": "net1"
},
{
"id": "5ty87a",
"type": "net",
"color": "yellow",
"host": "net2"
}
]
所以我想获取 X 字典
X=(
['yellow']="9b058640 5ty87a"
['red']="9b058640 0ui9k40"
)
我可以按值解析 :
jq -c '.[] | select(.color | contains("red"))'
您可以在 jq 中使用 @tsv
运算符来发出 tab-separated 输出,bash while read
循环可以轻松地将其解析为输入。
假设您的输入 JSON 在变量 s
中:
declare -A X=( )
while IFS=$'\t' read -r color id; do
X[$color]+="$id "
done < <(jq -r '.[] | [.color, .id] | @tsv' <<<"$s")
上面确实走了一个小捷径,因为它在每个项目之后留下了尾随 space。如果出于某种原因这是不可接受的,您可以随时遍历数组并在事后使用第二个循环清理它:
for color in "${!X[@]}"; do
X[$color]=${X[$color]%" "}
done
您可以在 https://replit.com/@CharlesDuffy2/IndigoRemoteEngineering
的沙箱中看到此 运行或者,使用 eval
:
#!/usr/bin/env bash
case $BASH_VERSION in ''|[0-3].*) echo "ERROR: bash 4.0+ required" >&2; exit 1;; esac
declare -A X=( )
eval "$(
jq -r '
reduce .[] as $item ({}; .[$item.color] += [$item.id])
| to_entries[]
| "X[\(.key | @sh)]=\(.value | join(" ") | @sh)"
'
)" <file.json
答案:
declare -A a
. <(jq -r '"a[\(.color)]+=${a[\(.color)]+ }\(.id)"' file.json)
提供:
$ declare -p a
$ declare -A a=([red]="9b058640 0ui9k40" [yellow]="0u858640 5ty87a" )
# or
$ echo "${a[red]}"; echo "${a[yellow]}"
9b058640 0ui9k40
0u858640 5ty87a
解释:
- 在
jq
中构造以下bash代码: a[color]+=${a[color]+ }id
${a[color]+ }
扩展为a[color]
为空时。"\(.id)"
是 jq 字符串插值 - 它被替换为.id
的值
- 使用
.
和一个进程子来获取此代码
不需要 .
、source
、eval
甚至 bash 中的循环。您只需要 declare
和 jq
,它们可以使用 escaping with @sh
and string interpolation:
declare -A X="($(
jq -r '
("yellow", "red") as $color
| @sh "[\($color)]=\(map(select(.color == $color).id) | join(" "))"
' input.json
))"
$ echo "${X[yellow]}"
0u858640 5ty87a
$ echo "${X[red]}"
9b058640 0ui9k40