jq如何获取所有值并用于脚本参数

4

我有一个vms.json文件

[
    {
        "name": "jms1",
        "port1": 24000,
        "port2": 25000,
        "port3": 26000
    },

    {
        "name": "jms2",
        "port1": 24001,
        "port2": 25000,
        "port3": 26001
    }
]

我有一个port_script.sh脚本,它接受[name] [port1] [port2] [port3]作为编号参数。

我可以运行这个jq命令来获取键,但我需要获取值。

jq -r -c '.[] |keys' vms.json
["port1","name","port2","port3"]
["port1","name","port2","port3"]

我不知道如何使用这个输出来获取相关的值。如果我能够得到这些值,我应该能够通过 xargs 将它们传递到我的脚本中。

非常感谢您的帮助!

附加信息:port_script.sh

#!/bin/bash
name=$1
port1=$2
port2=$3
port3=$4
vagrant ssh ${name} -- \
    -L ${port1}:127.0.0.1:${port1}  \
    -R ${port2}:127.0.0.1:${port2}  \
    -L ${port3}:127.0.0.1:${port3} 

应用 Santiago 和 Peak 的回应 - 这对我有用。
eval "$(jq -r '.[] | ["./port_script.sh"] + [.name, .port1, .port2, .port3 | @sh] | join(" ")' vms.json)"

你的脚本预期输出是什么? - user3899165
4个回答

3
采用 Santiago 的回答并进一步说明,您可以写成以下内容:
$ jq -r '.[] | "port_script.sh \"\(.name)\" \"\(.port1)\" \"\(.port2)\" \"\(.port3)\""'

通过您的输入,这将生成:

port_script.sh "jms1" "24000" "25000" "26000"
port_script.sh "jms2" "24001" "25000" "26001"

或者更好的方式可能是:
$ jq -r --arg command port_script.sh '.[] 
  | $command + " \"\(.name)\" \"\(.port1)\" \"\(.port2)\" \"\(.port3)\""'

如果每个对象中的键按照脚本所需的顺序排列,那么我们可以进一步概括一步,使得 jq 脚本适用于任何数量的键:
jq -nr --arg command port_script.sh '
  .[] | $command + " " + ([.[]] | @sh)'

port_script.sh 'jms1' 24000 25000 26000
port_script.sh 'jms2' 24001 25000 26001

很遗憾,jq目前还没有“system”子命令。

1
不必手动在每个参数周围添加引号,这很容易出错。您可以使用@sh过滤器:$ jq -r '.[] | "port_script.sh \(.name | @sh) \(.port1 | @sh) \(.port2 | @sh) \(.port3 | @sh)"' - user3899165
2
或者更简洁一些:jq -r '.[] | ["port_script.sh"] + [.name, .port1, .port2, .port3 | @sh] | join(" ")' - user3899165
这对我有用,我将最终解决方案添加到了上面的问题中。感谢@peak和SantiagoLapresta。 - mancocapac

1
你试图尝试的内容还不太清楚,但是这里有一些可能会有帮助的提示:
要获取脚本需要的值,只需使用“.foo”语法引用它们即可。例如,在给定的输入文件上运行“jq -r '.[].name'”将产生以下输出:
jms1
jms2

同样地,您可以引用它们的全部内容:

jq -r '.[] | .name, .port1, .port2, .port3'

jms1
24000
25000
26000
jms2
24001
25000
26001

如果你想像上面的例子一样把它们放在一个数组中,只需用方括号包裹它们:

jq -r -c '.[] | [.name, .port1, .port2, .port3]'

(注意:保留原文中的html标签)
["jms1", 24000, 25000, 26000]
["jms2", 24001, 25000, 26001]

希望这可以帮助你!但在不真正了解你的问题之前,我无法提供更多帮助。

我在上面添加了我的脚本。我正在使用json文件来配置一些Vagrant服务器。在VM运行后,相同的参数用于ssh端口转发/反向。因此,我想重复使用json值。 - mancocapac

0

最近我不得不处理这个问题,因为有人要求我在shell脚本中使用json。

所以对于将来遇到这个问题的任何人:

建议使用更本地的shell方法,而不是使用eval函数。

考虑使用循环和read命令。

#!/bin/bash
read -d '' JSON <<'EOF' || true
[
    {
        "name": "jms1",
        "port1": 24000,
        "port2": 25000,
        "port3": 26000
    },

    {
        "name": "jms2",
        "port1": 24001,
        "port2": 25000,
        "port3": 26001
    }
]
EOF

while IFS=$'\t' read name port1 port2 port3 eol ; do

    ./port_script.sh "$name" "$port1" "$port2" "$port3"

done <<<" $(echo "$JSON" | jq --raw-output '.[] | [.name, .port1, .port2, .port3] | @tsv' )"

注意:

如果你的 JSON 设计中可能包含空字段,这可能会成为一个问题。jq 的 @tsv 函数可以正确地使用制表符分隔所有内容。但是至少 Bash 会将多个分隔符视为一个。这仅适用于使用空格(" " 或 "\t")作为分隔符的情况。我通过使用以下方法解决了这个问题:

$(echo "$JSON" | jq --raw-output '.[] | [.name, .port1, .port2, .port3] | @tsv' | tr $'\011' $'\037' )  

这将用ASCII单元分隔符(八进制037)替换所有制表符(八进制011)。

我首先尝试只用'\t\t'替换为'\t[空格]\t',当您将它们用作port_script.sh的参数时,多个空格会再次被缩减为一个,这样就可以正常工作...但是这会扰乱任何条件测试(例如test -z)。

单元分隔符应该安全使用。如果jq可以直接实现这一点就好了....但是呢 :)

还有,更改您的while循环:

...
while IFS=$'\037' read name port1 port2 port3 eol ; do
...

-2

我知道在JavaScript中你可以这样做

array.forEach(function(a) {
     console.log(Object.keys(a));
});

Object.keys非常方便,因为它允许您获取对象的键。然后只需迭代数组并记录到控制台

https://jsfiddle.net/f8rbdn85/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接