Reputation: 882
I have this vms.json
[
{
"name": "jms1",
"port1": 24000,
"port2": 25000,
"port3": 26000
},
{
"name": "jms2",
"port1": 24001,
"port2": 25000,
"port3": 26001
}
]
I have a port_script.sh which takes [name] [port1] [port2] [port3] as numbered arguments.
I can run this jq, to get keys, but I need to get values.
jq -r -c '.[] |keys' vms.json
["port1","name","port2","port3"]
["port1","name","port2","port3"]
I don't know how to use this output to get the associated values. If I can get the values I should be able to pipe them to my script via xargs.
Thanks in advance for your help
added: 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}
Applying Santiago and Peak's responses - this works for me
eval "$(jq -r '.[] | ["./port_script.sh"] + [.name, .port1, .port2, .port3 | @sh] | join(" ")' vms.json)"
Upvotes: 4
Views: 2836
Reputation: 31
I had to deal with this lately because i was asked to use json in a shell script.
So for anyone finding this topic in the future:
Instead of using the eval function I would suggest using a more native shell approach.
Consider the use of a loop and the read command.
#!/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' )"
Note:
If, by desgin, your JSON could contain empty fields this can be a problem. The @tsv function of jq works correctly and separates everything with tabs. But at least the Bash will treat multiple delimiters as one. This is only true if you use whitespaces (" " or "\t") as separators. I worked around this by using:
$(echo "$JSON" | jq --raw-output '.[] | [.name, .port1, .port2, .port3] | @tsv' | tr $'\011' $'\037' )
This will replace all tabs (octal 011) with the ASCII Unit Separator (octal 037).
I first tried just replacing '\t\t' with '\t[space]\t' which would work fine as multiple spaces would once again get reduced to one when you use them as parameters to your port_script.sh... but this would throw off any conditional tests (test -z for example).
The Unit Separator should be safe to use for this. Would be nice if jq could implement this directly.... but yeah :)
And also change this for your while loop:
...
while IFS=$'\037' read name port1 port2 port3 eol ; do
...
Upvotes: 0
Reputation: 116870
Taking Santiago's answer one step further, you could write:
$ jq -r '.[] | "port_script.sh \"\(.name)\" \"\(.port1)\" \"\(.port2)\" \"\(.port3)\""'
With your input, this produces:
port_script.sh "jms1" "24000" "25000" "26000"
port_script.sh "jms2" "24001" "25000" "26001"
Or better yet perhaps:
$ jq -r --arg command port_script.sh '.[]
| $command + " \"\(.name)\" \"\(.port1)\" \"\(.port2)\" \"\(.port3)\""'
If the keys in each object are in the order required by the script, then we can generalize one step further so that the jq script will work for any number of keys:
jq -nr --arg command port_script.sh '
.[] | $command + " " + ([.[]] | @sh)'
port_script.sh 'jms1' 24000 25000 26000
port_script.sh 'jms2' 24001 25000 26001
(Unfortunately, jq does not have a system
subcommand, at least not yet.)
Upvotes: 3
Reputation:
What you're trying to attempt is pretty much unclear, but here's some possibly helpful pointers:
To get the values your script needs, just refer to them with the .foo
syntax. For example, running jq -r '.[].name'
over the given input file would produce the following output:
jms1
jms2
Similarly, you can refer to all of them:
jq -r '.[] | .name, .port1, .port2, .port3'
jms1
24000
25000
26000
jms2
24001
25000
26001
And, if you'd like them in an array as in the example above, just wrap them in brackets:
jq -r -c '.[] | [.name, .port1, .port2, .port3]'
["jms1", 24000, 25000, 26000]
["jms2", 24001, 25000, 26001]
Hope this helps! That's as far as I can go without really understanding your problem.
Upvotes: 1
Reputation: 93
I know in JavaScript you could do this
array.forEach(function(a) {
console.log(Object.keys(a));
});
Object.keys is particularly handy because it lets you get keys of an object. Then just iterate over an array and log to console
https://jsfiddle.net/f8rbdn85/
Upvotes: -2