Using yq in for loop bash
Asked Answered
D

8

13

I have a yaml array like below,

identitymappings:
- arn: "arn:aws:iam::12345567:role/AdmRole"
  group: "system:masters"
  user: "user1"
- arn: "arn:aws:iam::12345567:role/TestRole"
  group: "system:masters"
  user: "user2"

I am trying to parse this yaml in a bash script using for loop and yq.

 for identityMapping in $(yq read test.yaml "identitymappings[*]"); do
      roleArn=$identityMapping["arn"]
      group=$identityMapping.group
      user=$identityMapping.user
done

But I am not getting the expected results like not able to fetch the values of roleArn,group,user. Please let me know how to fix this.

Deluca answered 14/7, 2020 at 15:44 Comment(1)
This is very closely related to iterate over json with jq.Strickler
M
23

The way I would do it is:

# load array into a bash array
# need to output each entry as a single line
readarray identityMappings < <(yq e -o=j -I=0 '.identitymappings[]' test.yml )

for identityMapping in "${identityMappings[@]}"; do
    # identity mapping is a yaml snippet representing a single entry
    roleArn=$(echo "$identityMapping" | yq e '.arn' -)
    echo "roleArn: $roleArn"
done

output:

roleArn: arn:aws:iam::12345567:role/AdmRole
roleArn: arn:aws:iam::12345567:role/TestRole

Disclaimer: I wrote yq

Monique answered 29/11, 2021 at 22:58 Comment(3)
note that the latest yq now also has a '@tsv' operator (so you can do it the way @charles-duffy posted without needing jq).Monique
The disclaimer though xD.Hemicycle
yq e .identitymappings[] file.ymlTronna
A
6

I wasn't able to comment on Charles Duffy's proper answer, but this works for yq v4 without the use of jq...

while IFS=$'\t' read -r roleArn group user _; do
  echo "Role:  $roleArn"
  echo "Group: $group"
  echo "User:  $user"
done < <(yq e '.identitymappings[] | [.arn, .group, .user] | @tsv' test.yaml)
Afternoon answered 25/1, 2022 at 0:2 Comment(1)
Good to know; BTW, TSV values might be escaped (with \t \\ \n etc...); you can unescape them with for example printf -v roleArn %b "$roleArn"Olvera
C
4

There is an improvement of @Rad4's answer that worked for me.

You can neatly loop through using latest yq and jq via:

for im in $(yq eval -o=j test.yaml | jq -cr '.identitymappings[]'); do
      arn=$(echo $im | jq -r '.arn' -)
      group=$(echo $im | jq -r '.group' -)
      user=$(echo $im | jq -r '.user' -)
      echo $arn $group $user
done

This loops through valid online jsons, which makes jq still work inside the loop.

Cartier answered 10/10, 2021 at 12:42 Comment(0)
C
3
  1. Get identitymappings length
  2. Using index to access element of identitymappings
array_length=`yq e ". identitymappings | length - 1" test.yaml`

if [ $array_length -le 0 ] ; then
  exit
fi

for element_index in `seq 0 $array_length`;do
    arn=`yq e ".identitymappings[$element_index]. arn" test.yml`
    group=`yq e ".identitymappings[$element_index]. group" test.yml`
    user=`yq e ".identitymappings[$element_index]. user" test.yml`
done
  1. How to get length of array in yq?
Cad answered 5/8, 2022 at 1:3 Comment(1)
I find this approach the most straight forward. It allows to utilise yq for parsing data, instead of worrying about the outputs of yq and reading them in bash. The only thing I would change is replace backticks with $()Rudimentary
S
2

The easiest way to read from jq or yq into bash is to use a BashFAQ #1 while read loop to handle line-oriented data; in the below, we use @tsv to generate line-oriented output:

while IFS=$'\t' read -r roleArn group user _; do
  echo "Role:  $roleArn"
  echo "Group: $group"
  echo "User:  $user"
done < <(yq -j read test.yaml \
         | jq -r '.identitymappings[] | [.arn, .group, .user] | @tsv')

Note that if you were using the Python yq rather than the Go one, you could remove the yq -j read and just use yq -r '...' in place of jq -r '...'.

Strickler answered 14/7, 2020 at 16:29 Comment(0)
A
1

The answer by @mike.f is a good one. However, it does not work on OSX machines, because readarray is not an available command. You can read more about this here.

Here is the equivalent that would work on a mac:

# load array into a bash array
# need to output each entry as a single line
identitymappings=( $(yq e -o=j -I=0 '.identitymappings[]' test.yml ) )

for identityMapping in "${identityMappings[@]}"; do
    # identity mapping is a yaml snippet representing a single entry
    roleArn=$(echo "$identityMapping" | yq e '.arn' -)
    echo "roleArn: $roleArn"
done
Alterable answered 14/3, 2022 at 19:48 Comment(0)
O
0

My solution, using yq map function:

  yq e '.identitymappings | map([.arn, .group, .user] | join(" ")) | .[]' file.yaml \
| while read -r arn group user; do
    echo $arn
  done
Ornithic answered 14/6 at 14:27 Comment(0)
D
-1

I figured out..

for identityMapping in $(yq read test.yaml -j "identitymappings[*]"); do
      echo $identityMapping
      roleArn= echo $identityMapping | jq -r '.arn'
      echo $roleArn
      group= echo $identityMapping | jq -r '.group'
      echo $group
      user= echo $identityMapping | jq -r '.user'
      echo $user
Deluca answered 14/7, 2020 at 16:22 Comment(3)
No. This isn't assigning values to roleArn, group or user at all. Your output isn't written from the echos, it's written by the jq commands.Strickler
That's because roleArn= echo $identityMapping | jq -r '.arn' doesn't assign to roleArn, it's just setting roleArn as an environment variable with an empty value during the execution of echo.Strickler
...see How do I set a variable to the output of a command in bash?Strickler

© 2022 - 2024 — McMap. All rights reserved.