0

I am trying to create a unix shell/bash function to get a specific value from a YAML file. I don't want to use Python/Ruby or other scripting languages or do not want to install any packages that are not natively available in bash. The function should take the property name name and yaml file name as input and return the value of the property.

Sample YAML is given below.

---
Action: start
Version: 642
Domains:
  Domain:
    Name: SanityTest
    AppSpaces:
      AppSpace:
        Name: SanityAppSpace
        AppNodes:
          AppNode:
            Name: SanityAppnode
            Applications:
              Application:
                Name: InstagramTest.application
                Version: "1.0"
          AppNode:
            Name: SanityAppnode_1
            Applications:
              Application:
                Name: InstagramTest.application
                Version: "1.0"

I am looking for some function that works like:

DomainName=getValue Domains_Domain[1]_Name /path/my_yaml.yml

AppSpaceName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[1]_Name /path/my_yaml.yml

AppNodeName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[1]_Name /path/my_yaml.yml

AppName=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[$AppNodeName]_Applications_Application[1]_Name /path/my_yaml.yml

AppVersion=getValue Domains_Domain[$DomainName]_AppSpaces_AppSpace[$AppSpaceName]_AppNodes_AppNode[$AppNodeName]_Applications_Application[$AppName]_Version /path/my_yaml.yml

I came up with the below code so far, but it is not working as expected. Appreciate any help. Please advise if my approach is wrong and if there is better way.

function getValue {
    local ymlFile=$2
    local Property=$1
    
    IFS='_' read -r -a props_tokens <<< "${Property}"
    
    local props_index=0
    echo "${props_tokens[props_index]}"
    
    CheckingFor="Domains"
    currEntity_Index=0
    
    while IFS= read -r line; do
        curr_prop="${props_tokens[props_index]}"
        curr_prop_Index=0
        
        IFS=':' read -r -a curr_line_props <<< "${line}"
        curr_line_prop = ${curr_line_props[0]}
        
        if [ "${props_tokens[props_index]}" == *"["*"]"* ]
        then
            IFS='[' read -r -a prop_tokens <<< "${props_tokens[props_index]}"
            curr_prop=${prop_tokens[0]}
            curr_prop_Index=${prop_tokens[1]::-1}
            
            echo "Index processed"
            echo $curr_prop
            echo $curr_prop_Index
        else echo "No Index for this property"
        fi
        
        if [ "$curr_line_prop" != "$CheckingFor" ]
        then continue
        fi
        
        if [ "|Action|Name|Version|" == *"$curr_prop"* ]
        then
            return curr_line_props[1]
        fi
        
        if [ "$curr_prop" == "$CheckingFor" ]
        then
            if [ "|Domains|AppSpaces|AppNodes|Applications|" == *"$curr_prop"* ]
            then
                props_index++
                case $CheckingFor in
                    Domains)
                        CheckingFor="Domain";;
                    Domain)
                        CheckingFor="AppSpaces";;
                    AppSpaces)
                        CheckingFor="AppSpace";;
                    AppSpace)
                        CheckingFor="AppNodes";;
                    AppNodes)
                        CheckingFor="AppNode";;
                    AppNode)
                        CheckingFor="Applications";;
                    Applications)
                        CheckingFor="Application";;
                    *) echo "$curr_prop - not switched";;
                esac
                continue
            else
                if [ "$curr_prop_Index" == 0 ]
                then
                    case $CheckingFor in
                        Domains)
                            CheckingFor="Domain";;
                        Domain)
                            CheckingFor="AppSpaces";;
                        AppSpaces)
                            CheckingFor="AppSpace";;
                        AppSpace)
                            CheckingFor="AppNodes";;
                        AppNodes)
                            CheckingFor="AppNode";;
                        AppNode)
                            CheckingFor="Applications";;
                        Applications)
                            CheckingFor="Application";;
                        *) echo "$curr_prop - not switched";;
                    esac
                    props_index++
                    continue
                else
                    Integer_regex='^[0-9]+$'
                    if [[ $curr_prop_Index =~ $Integer_regex ]]
                    then
                        # Iterate until we get to the nth Item
                        currEntity_Index++
                        if [ $currEntity_Index == $curr_prop_Index ]
                        then
                            case $CheckingFor in
                                Domains)
                                    CheckingFor="Domain";;
                                Domain)
                                    CheckingFor="AppSpaces";;
                                AppSpaces)
                                    CheckingFor="AppSpace";;
                                AppSpace)
                                    CheckingFor="AppNodes";;
                                AppNodes)
                                    CheckingFor="AppNode";;
                                AppNode)
                                    CheckingFor="Applications";;
                                Applications)
                                    CheckingFor="Application";;
                                *) echo "$curr_prop - not switched";;
                            esac
                            currEntity_Index=0
                        else
                        fi
                        props_index++
                        continue
                    else
                        # Iterate until the name of the entity match and continue with next property token
                        # How to handle if the search sipllied into next object?
                    fi
                fi
            fi
        else
            props_index++
            continue
        fi
    done < $ymlFile
    return "${props_tokens[props_index]}"
}
AKPuvvada
  • 47
  • 2
  • 10
  • 2
    The proper Right Thing is to use a real YAML parser -- either one from a tool built for the job like `yq`, or to use a different language like Python/Ruby/JavaScript/Go/Rust/etc that has proper YAML libraries available. – Charles Duffy Nov 24 '21 at 19:04
  • YAML is a significantly more complicated format than the examples you're looking at so far -- for example, all valid JSON is _also_ valid YAML, so every YAML parser needs to also be a JSON parser. There's no real attempt in this code to even parse JSON, much less the wide array of equivalent forms YAML offers. – Charles Duffy Nov 24 '21 at 19:05
  • Thanks, @CharlesDuffy. Can we extend the script at https://github.com/jasperes/bash-yaml to achieve what I am looking for? Any comments? – AKPuvvada Nov 24 '21 at 19:28
  • The subset of YAML that script tries to support (it makes no attempt to support the entire format) does, at a glance, look a lot like the subset used in your sample data. So if instead of really supporting _all_ YAML you only want to support _some_ YAML-ish data, that may well be a place to start. – Charles Duffy Nov 24 '21 at 20:02
  • 1
    BTW, if it seems like I'm coming in with a somewhat hostile perspective -- one set of very unpleasant experiences in my past is from having customers that hand-built a JSON "parser" instead of using a real one, and then complained every time an API my company provided changed its output in a way that broke their so-called "parser", despite the output always being valid, semantically-equivalent JSON, such that any tool properly designed for the job wouldn't have noticed the difference at all. Substituting hand-rolled parsers for compliant ones built-to-spec makes problems for others to inherit. – Charles Duffy Nov 24 '21 at 20:05
  • @CharlesDuffy, I understand. I am not looking for a generic script that can process all possible YAML files. I am looking for something that works for my scenario. I just need to convert a CSV based script to YAML based one and I was told not to install anything that is not available already on the machine. – AKPuvvada Nov 24 '21 at 21:26
  • What does "natively available in bash" mean? – William Pursell Nov 24 '21 at 21:41
  • 2
    If you want to convert *to* YAML, why do you need to *read* YAML? Generally the proper solution is to slap whoever defined the „read YAML without installing anything“ requirement because that's how high-maintenance code is created. However if the target system is known, you could get away with embedding the [yq utility binary](https://github.com/mikefarah/yq) right [into your script](https://stackoverflow.com/q/49351437/347964) and then just calling that. – flyx Nov 25 '21 at 00:14
  • @flyx, I never embedded a binary in a shell script. How can I do that? – AKPuvvada Dec 06 '21 at 18:21
  • I found https://www.xmodulo.com/embed-binary-file-bash-script.html. Will try that. Thanks. – AKPuvvada Dec 06 '21 at 18:25

0 Answers0