Search code examples
jsonbashyamljqyq

Read in multi-level YAML / JSON to bash variables


Say I have a yaml file, example.yaml:

networks:
  net:
    driver: overlay
dockerfiles:
  nodeapp: |
    FROM node:18-alpine
    WORKDIR /$NAME
services:
  registry:
    image: registry:2
    ports:
      - "5000:5000"
      - "337:337"
  pinger:
    build:
      context: .
      dockerfile: nodeapp
    deploy:
      replicas: 3

I can convert to JSON using yq, and then set the top level entries as variables with jq:

eval $(yq e example.yaml -j | jq -r '..? | to_entries | .[] | .key + "=" + (.value|tostring)')

echo $services
{registry:{image:registry:2,ports:[5000:5000]},pinger:{build:{context:.,dockerfile:nodeapp},deploy:{replicas:3}}}

However, I cannot seem to get jq to recursively go into the objects and set variables, so that echo $services_pinger_dockerfile gives "nodeapp" for example.

I've seen in the docs that there is a recursion operator. Can anyone suggest a way of doing this?


There are similar questions. But none that I found dealing with multilevel YAML/JSON to shell variables in this way.

jq: Getting two levels of keys

Extract JSON value to shell variable using jq

https://unix.stackexchange.com/questions/413878/json-array-to-bash-variables-using-jq

https://unix.stackexchange.com/questions/121718/how-to-parse-json-with-shell-scripting-in-linux

Parsing JSON with Unix tools

How can I parse a YAML file from a Linux shell script?

How to read yaml file into bash associative array?


Solution

  • Why not process it directly with yq?

    eval $(yq e '.. | select(. == "*") | {(path | join("_")): . style="double"} ' example.yaml | sed -E 's/(.*): (.*)/\1=\2/g')
    echo $services_pinger_build_dockerfile
    

    yq command was taken from this answer, see there for details.

    The appended sed rewrites a_b: "c" into a_b="c", so we can then eval it in bash. I do not use the technique you show because it would cause the values to not be quoted in the output, which is bad especially for multi-line strings.

    The code outputs nodeapp.