Reputation: 4013
I am writing a bash script that reads a JSON string then loop based on the JSON values to execute a CLI command.
#!/usr/bin/env bash
jq --version > /dev/null 2>&1 || { echo >&2 "jq is required but it's not installed. Aborting."; exit 1; }
read -r -d '' USER_ACTIONS << EOM
{
"user1": [
"action1"
],
"user2": [
"action2",
"action3"
]
}
EOM
USERS= #TODO
for user in USERS; do
ACTIONS= #TODO
for action in ACTIONS; do
echo "Executing ${command} ${user}-${action}"
done
done
If jq is present in the server, how do I populate the USERS and ACTIONS variable?
Upvotes: 0
Views: 324
Reputation: 22012
Would you please try the following:
#!/bin/bash
declare -a users # array of users
declare -A actions # array of actions indexed by user
read -r -d '' user_actions << EOM
{
"user1": [
"action1"
],
"user2": [
"action2",
"action3"
]
}
EOM
while IFS=$'\t' read -r key val; do
users+=( "$key" ) # add the user to the array "users"
actions[$key]="$val" # associate the actions with the user
done < <(jq -r 'to_entries[] | [.key, .value[]] | @tsv' <<< "$user_actions")
for user in "${users[@]}"; do # loop over "users"
IFS=$'\t' read -r -a vals <<< "${actions[$user]}"
for action in "${vals[@]}"; do
echo yourcommand "$user" "$action"
done
done
Output:
yourcommand user1 action1
yourcommand user2 action2
yourcommand user2 action3
[Explanations]
jq
command outputs TSV which looks like:user1\taction1
user2\taction2\taction3
where \t
represents a tab character used as a field delimiter.
read
builtin command assigns key
to the first field and val
to the remaining field(s). If val
contains two or more fields,
it will be splitted with tne next read
builtin in the next loop.echo
and replace the string yourcommand
with the command name.Upvotes: 0
Reputation: 36033
Depending on what command you want to execute, if it can be performed from within jq, it's easier to also move the loop inside. There are several ways to accomplish that. Here are some examples, all yielding the same output:
jq -r 'to_entries[] | "Executing command \(.key)-\(.value[])"' <<< "$USER_ACTIONS"
jq -r 'keys[] as $user | "Executing command \($user)-\(.[$user][])"' <<< "$USER_ACTIONS"
jq -r --stream '"Executing command \(.[0][0])-\(.[1]? // empty)"' <<< "$USER_ACTIONS"
Output:
Executing command user1-action1
Executing command user2-action2
Executing command user2-action3
Upvotes: 1
Reputation: 184965
It seems better to play with vanilla js (nodejs
):
const myvar={
"user1": [
"action1"
],
"user2": [
"action2",
"action3"
]
};
for (let user in myvar) {
myvar[user].forEach((action) => {
console.log("Executing command " + user + "-" + action);
});
}
Executing command user1-action1
Executing command user2-action2
Executing command user2-action3
node script.js
You can remove Executing string, then:
node script.js | bash
Upvotes: 0