0

I have these bash functions for loading an INI file and parsing it into two arrays one for keys and the second for values then use these arrays to write a new INI file

declare -a ini_keys
declare -a ini_values

load_settings() {
    echo "Loading settings from \"$1\""
    ini_keys=()
    ini_values=()
    if [ -f "$1" ]; then
        ini_keys=($(awk -F ' *= *' '/^[a-z]/ {print $1}' "$1"))
        ini_values=($(awk -F ' *= *' '/^[a-z]/ {print $2}' "$1"))
        return 0
    fi
    echo "Missing \"$1\""
    return 1
}

save_settings() {
    echo "Saving settings to \"$1\""
    [ -f "$1" ] && mv "$1" "$1.bak"
    for index in "${!ini_keys[@]}"; do
        echo "${ini_keys[$index]}=${ini_values[$index]}" >> "$1"
    done
}

load_settings config.ini
save_settings new-config.ini

The problem I'm having is that sometimes I got some empty values

config.ini

key_01=value_01
key_02=value_02
key_03=
key_04=value_04
key_05=value_05

So I end up with a "new-config.ini" like

key_01=value_01
key_02=value_02
key_03=value_04
key_04=value_05
key_05=

How can I fix this to preserve the empty values in array and in new written INI file?

1 Answers1

1
ini_values=($(awk -F ' *= *' '/^[a-z]/ {print $2}' "$1"))

You're relying on word splitting here, which indeed does remove empty fields, but will also lead into problems if any of your values have whitespace; they'll get split into multiple fields. And glob characters may expand to filenames. Discussed somewhat here: Why does my shell script choke on whitespace or other special characters?

Also, running an awk instance or two for every input line is rather a high overhead.

Luckily, the shell's read builtin can split input on the =, so you can do something like below. Here, with associative arrays, since they can represent key-value relations directly. But you could use regular arrays if you really want.

#!/bin/bash

configfile=config.ini
declare -A config

# read the ini file in
while IFS== read -r key val; do
    config+=(["$key"]="$val")
done < "$configfile"

# print and change one value
k=key_05
echo "key $k -> value ${config[$k]}"
config[$k]="foo bar"

# print it all out
for key in "${!config[@]}"; do
    printf "%s=%s\n" "$key" "${config[$key]}"
done
ilkkachu
  • 133,243
  • 15
  • 236
  • 397