0

I am using the script from here in order to read a yaml file that contains some local paths of my system. The script does what is intended to do but I would also like to keep count of how many paths are there.

YAML example:

path1: ../
path2: /bin
path3: ../src

.sh example:

#!/bin/bash

paths=0;

parse_yaml() {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      echo $indent
      for (i in vname) {if (i > indent) {delete vname[i]}}
      if (length($3) > 0) {
         vn=""; for (i=0; i<indent; i++) {
         vn=(vn)(vname[i])("_")
     }
         printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
     printf(vn);
      }
   }'
}

eval $(parse_yaml paths.yaml)



echo $path1
echo $path2
echo $path3

OUTPUT:

../
/bin
../src

I would like to have in paths variable declared at top saved the number of keys(in other words paths) declared in that yaml file. I tried to increment it inside the for loops but it did not work.

Andy95
  • 210
  • 1
  • 10
  • 1
    Isn't this just `wc -l paths.yaml`? – Barmar Sep 19 '22 at 23:09
  • `wc -l` is working, but I kind of need to save just the number in paths variable. Right now I am saving `3 paths.yaml`. Later on I want to write a for loop with the paths as the max loops number. – Andy95 Sep 20 '22 at 07:34
  • 2
    If you really do not want to use a proper parser (as per the comment by @oguzismail), then it seems like it might be easier to read the paths into an array: `paths=( $(awk '{print $2}' sample.yaml) )`. Then you have access to the number of paths `printf "Number of paths: %s\n" "${#paths[@]}"` and each parsed path: `printf "%s\n" "${paths[@]}"`1 – j_b Sep 20 '22 at 21:39
  • @j_b, This worked. Please formulate an answer and I will mark it as correct. – Andy95 Sep 26 '22 at 08:39
  • 2
    BTW, bash and sh are two different languages. Just as the C and C++ communities frown on using both tags in the same question, please pick one or the other here (even on operating systems where `/bin/sh` is a symlink to `bash`, it disables some features for better compatibility when started under the `sh` name). – Charles Duffy Sep 26 '22 at 12:13
  • 2
    ...in particular, one of the features that isn't guaranteed to be available to `sh` is arrays, so if you really do need `/bin/sh` compatibility, any answer using arrays is unsuitable. – Charles Duffy Sep 26 '22 at 12:14
  • I removed the sh tag. Thanks! – Andy95 Sep 26 '22 at 13:10

1 Answers1

1

If you cannot use a proper YAML parser, one option would be to read the paths into an array:

IFS=$'\n' read -r -d '' -a paths < <(awk '{print $2}' sample.yaml  && printf '\0')
printf "Number of paths: %s\n" "${#paths[@]}" 

To print each path:

printf "%s\n" "${paths[@]}"
j_b
  • 1,975
  • 3
  • 8
  • 14
  • 1
    That's not the best way to read into an array -- see [BashPitfalls #50](https://mywiki.wooledge.org/BashPitfalls#hosts.3D.28_.24.28aws_.2BICY.29_.29). `readarray -t paths < <(awk '{print $2}' sample.yml)` works better if any of the values might otherwise be munged by wordsplitting, glob expansion, etc. – Charles Duffy Sep 26 '22 at 12:11
  • Thanks for the link @CharlesDuffy. I updated my answer per your suggestion. – j_b Sep 26 '22 at 12:23
  • 1
    If you're going to use `-d ''`, I'd suggest `awk '{print $2}' sample.yml && printf '\0'` inside the process substitution -- with the printf creating the NUL that `read` expects to terminate its input, `read` will have a successful exit status only if awk does. Right now it'll fail whenever the input stream doesn't end with a NUL, messing up anyone using `set -e`, ERR traps, returning from a function directly after this command (and thus passing through its exit status), etc. – Charles Duffy Sep 26 '22 at 12:53