Read a config file in BASH without using “source”

后端 未结 4 1973
南方客
南方客 2021-02-01 07:45

I\'m attempting to read a config file that is formatted as follows:

USER = username
TARGET = arrows

I realize that if I got rid of the spaces,

相关标签:
4条回答
  • 2021-02-01 08:02

    The following script iterates over each line in your input file (vars in my case) and does a pattern match against =. If the equal sign is found it will use Parameter Expansion to parse out the variable name from the value. It then stores each part in it's own array, name and value respectively.

    #!/bin/bash
    
    i=0
    while read line; do
      if [[ "$line" =~ ^[^#]*= ]]; then
        name[i]=${line%% =*}
        value[i]=${line#*= }
        ((i++))
      fi
    done < vars
    
    echo "total array elements: ${#name[@]}"
    echo "name[0]: ${name[0]}"
    echo "value[0]: ${value[0]}"
    echo "name[1]: ${name[1]}"
    echo "value[1]: ${value[1]}"
    echo "name array: ${name[@]}"
    echo "value array: ${value[@]}"
    

    Input

    $ cat vars
    sdf
    USER = username
    TARGET = arrows
    asdf
    as23
    

    Output

    $ ./varscript
    total array elements: 2
    name[0]: USER
    value[0]: username
    name[1]: TARGET
    value[1]: arrows
    name array: USER TARGET
    value array: username arrows
    
    0 讨论(0)
  • 2021-02-01 08:07

    First, USER is a shell environment variable, so it might be better if you used something else. Using lowercase or mixed case variable names is a way to avoid name collisions.

    #!/bin/bash
    configfile="/path/to/file"
    shopt -s extglob
    while IFS='= ' read lhs rhs
    do
        if [[ $lhs != *( )#* ]]
        then
            # you can test for variables to accept or other conditions here
            declare $lhs=$rhs
        fi
    done < "$configfile"
    

    This sets the vars in your file to the value associated with it.

    echo "Username: $USER, Target: $TARGET"
    

    would output

    Username: username, Target: arrows

    Another way to do this using keys and values is with an associative array:

    Add this line before the while loop:

    declare -A settings
    

    Remove the declare line inside the while loop and replace it with:

        settings[$lhs]=$rhs
    

    Then:

    # set keys
    user=USER
    target=TARGET
    # access values
    echo "Username: ${settings[$user]}, Target: ${settings[$target]}"
    

    would output

    Username: username, Target: arrows

    0 讨论(0)
  • 2021-02-01 08:09

    Thanks SiegeX. I think the later updates you mentioned does not reflect in this URL.

    I had to edit the regex to remove the quotes to get it working. With quotes, array returned is empty.

    i=0
    while read line; do
      if [[ "$line" =~ ^[^#]*= ]]; then
        name[i]=${line%% =*}
        value[i]=${line##*= }
        ((i++))
      fi
     done < vars
    

    A still better version is .

    i=0
    while read line; do
    if [[ "$line" =~ ^[^#]*= ]]; then
            name[i]=`echo $line | cut -d'=' -f 1`
                value[i]=`echo $line | cut -d'=' -f 2`
            ((i++))
    fi
    done < vars
    

    The first version is seen to have issues if there is no space before and after "=" in the config file. Also if the value is missing, i see that the name and value are populated as same. The second version does not have any of these. In addition it trims out unwanted leading and trailing spaces.

    This version reads values that can have = within it. Earlier version splits at first occurance of =.

    i=0
    while read line; do
    if [[ "$line" =~ ^[^#]*= ]]; then
            name[i]=`echo $line | cut -d'=' -f 1`
                value[i]=`echo $line | cut -d'=' -f 2-`
            ((i++))
    fi
    done < vars
    
    0 讨论(0)
  • 2021-02-01 08:10

    I have a script which only takes a very limited number of settings, and processes them one at a time, so I've adapted SiegeX's answer to whitelist the settings I care about and act on them as it comes to them.

    I've also removed the requirement for spaces around the = in favour of ignoring any that exist using the trim function from another answer.

    function trim()
    {
        local var=$1;
        var="${var#"${var%%[![:space:]]*}"}";   # remove leading whitespace characters
        var="${var%"${var##*[![:space:]]}"}";   # remove trailing whitespace characters
        echo -n "$var";
    }
    
    while read line; do
        if [[ "$line" =~ ^[^#]*= ]]; then
            setting_name=$(trim "${line%%=*}");
            setting_value=$(trim "${line#*=}");
    
            case "$setting_name" in
                max_foos)
                    prune_foos $setting_value;
                ;;
                max_bars)
                    prune_bars $setting_value;
                ;;
                *)
                    echo "Unrecognised setting: $setting_name";
                ;;
            esac;
        fi
    done <"$config_file";
    
    0 讨论(0)
提交回复
热议问题