0

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?

3 Answers 3

1

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
Sign up to request clarification or add additional context in comments.

Comments

0

It seems better to play with vanilla (nodejs):

const myvar={
    "user1": [
        "action1"
    ],
    "user2": [
        "action2",
        "action3"
    ]
};

for (let user in myvar) {
     myvar[user].forEach((action) => {
        console.log("Executing command " + user + "-" + action);
    });
}

Output

Executing command user1-action1
Executing command user2-action2
Executing command user2-action3

Usage

node script.js

Usage with bash

You can remove Executing string, then:

node script.js | bash

1 Comment

Hi giles, thanks for the answer. Yes, I considered using nodejs. But couple of reasons: 1) It uses a lot of linux command and existing binaries, I hide those to simplify the questions 2) CMIIW, installing jq is much lighter than installing whole nodejs.
0

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]

  • The jq command outputs TSV which looks like:
user1\taction1
user2\taction2\taction3

where \t represents a tab character used as a field delimiter.

  • The first 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.
  • if the output looks good, drop echo and replace the string yourcommand with the command name.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.