#!/bin/bash set -euo pipefail # ===================================================== # bash-ini v1.0.0 | (c) 2024 DrMaxNix | www.drmaxnix.de # ===================================================== # # Parse a ini config file. # # @param $1 Path to ini file. # @param $2 (Optional) prefix for output variable names. # ini_parse(){ ## VALIDATE PATH ## # get from param local path="$1" # make sure it is readable if [[ ! -r "$path" ]]; then ini_log_error "Unable to read config file '$path'" fi ## VAR PREFIX ## # get from param or fallback local var_prefix="${2:-ini}" # validate format if [[ ! "$var_prefix" =~ ^[a-z0-9]+(_[a-z0-9]+)*$ ]]; then ini_log_error "Invalid variable prefix '$var_prefix'" fi # build global var names local -n out_section_list="${var_prefix}" local line local section_name while IFS= read -r line; do ## TRIM WHITESPACES ## line=$(echo "$line" | sed -E "s/^\s*//g" | sed -E "s/\s*$//g") ## IGNORE LINES ## # empty lines if [[ "$line" == "" ]]; then # ignore continue; fi # comments if [[ "$line" =~ ^# || "$line" =~ ^\; ]]; then # ignore continue; fi ## MATCH SECTION HEADER ## if [[ "$line" =~ ^\[.*\]$ ]]; then # get section name section_name=$(echo "$line" | sed -E "s/^\[(.*)\]$/\1/g") # trim whitespaces section_name=$(echo "$section_name" | sed -E "s/^\s*//g" | sed -E "s/\s*$//g") # validate format if [[ ! "$section_name" =~ ^[A-Za-z0-9]+([\._-][A-Za-z0-9]+)*$ ]]; then ini_log_error "Invalid section name '$section_name'" fi # rewrite section name section_name=$(echo "$section_name" | tr 'A-Z' 'a-z' | tr '-' '_' | tr '.' '_') # maybe add to section list local found="no" for s in ${out_section_list[*]:-}; do if [[ "$s" == "$section_name" ]]; then found="yes"; break; fi done if [[ "$found" != "yes" ]]; then out_section_list=(${out_section_list[@]:-""} "$section_name") fi # declare section map section_map_varname="${var_prefix}__${section_name}" declare -g -A ${section_map_varname} continue; fi ## MATCH ASSIGNMENT ## if [[ "$line" =~ ^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[[:space:]\t]*=[[:space:]\t]*(.*)$ ]]; then # maybe enter root section if [[ ! "${section_name:+x}" ]]; then section_name="_root" out_section_list=(${out_section_list[@]:-""} "$section_name") section_map_varname="${var_prefix}__${section_name}" declare -g -A ${section_map_varname} fi # get key and value local key=$(echo "$line" | sed -E "s/^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[ \t]*=[ \t]*(.*)$/\1/g") local value=$(echo "$line" | sed -E "s/^([A-Za-z0-9]+([\._-][A-Za-z0-9]+)*)[ \t]*=[ \t]*(.*)$/\3/g") # maybe trim quote marks if [[ "$value" =~ ^\".*\"$ ]]; then value=$(echo "$value" | sed -E "s/^\"(.*)\"$/\1/g") elif [[ "$value" =~ ^\'.*\'$ ]]; then value=$(echo "$value" | sed -E "s/^'(.*)'$/\1/g") fi # maybe add key to key list local -n out_section_key_list="${var_prefix}__${section_name}__key_list" local found="no" for k in ${out_section_key_list[*]:-}; do if [[ "$k" == "$key" ]]; then found="yes"; break; fi done if [[ "$found" != "yes" ]]; then out_section_key_list=(${out_section_key_list[@]:-""} "$key") fi # set value in section map local -n out_section_map="${var_prefix}__${section_name}" out_section_map[$key]="$value" continue; fi ## UNKNOWN ## ini_log_error "Unknown config string '$line'" done < "$path" } # # HELPER: try calling external logger, else use fallback. # # @param $1 Message to print. # ini_log_debug(){ if [[ "$(type -t log_debug)" == "function" ]]; then log_debug "$1"; return; fi echo "[DEBUG] $1" 1>&2 } ini_log_info(){ if [[ "$(type -t log_info)" == "function" ]]; then log_info "$1"; return; fi echo "[INFO ] $1" 1>&2 } ini_log_warn(){ if [[ "$(type -t log_warn)" == "function" ]]; then log_warn "$1"; return; fi echo "[WARN ] $1" 1>&2 } ini_log_error(){ if [[ "$(type -t log_error)" == "function" ]]; then log_error "$1"; return; fi echo "[ERROR] $1" 1>&2 exit 1 }