Skip to content

Instantly share code, notes, and snippets.

@egladman
Created June 24, 2020 13:46
Show Gist options
  • Save egladman/e2f3b0ee391cba1c7deb12452799327e to your computer and use it in GitHub Desktop.
Save egladman/e2f3b0ee391cba1c7deb12452799327e to your computer and use it in GitHub Desktop.
parse ini files with pure bash. Read more about it here: eli.gladman.cc/blog/2020/06/23/parsing-ini-file-with-bash.html
parse_ini() {
# Usage: parse_ini "path/to/file"
# parse_ini "path/to/file" false
# By default variables will be evaluated. Disable this by passing in "false"
local regex_contains_variable="^([a-zA-Z0-9_]{1,})([[:space:]]\=[[:space:]])(.*)$"
local regex_contains_wrapped_quotes="^([\"\'])(.*)([\"\'])$"
local regex_contains_inside_quotes="^([\"])(.*)([\"])(.*)([\"])$"
local multi_line=""
while IFS='' read -r line || [[ -n "$line" ]]; do
# Strip trailing comments from line
if [[ "$line" == *[[:space:]]\#* ]]; then
line="${line%%\#*}"
fi
case "$line" in
\#*|\;*)
# Ignore commented line
continue
;;
\[*|*\])
# There is no explicit "end of section" delimiter; sections end at
# the next section declaration
section="${line##\[}" # Strip left bracket
section="${section%%\]}_" # Strip right bracket
continue
;;
*\\)
multi_line+="${line%%\\}\n"
;;
*)
if [[ -n "$multi_line" ]]; then
multi_line+="$line"
line="$multi_line"
multi_line=""
fi
;;
esac
if [[ ! "$line" =~ $regex_contains_variable ]] || [[ -n "$multi_line" ]]; then
continue
fi
local name val
val="${line/*\= }" # Everything after the equals sign
name="${section}${line/$val}" # Invert match. This way we know we've captured everything
name="${name%% *}" # Remove trailing whitespace and equals sign
# Wrap 'val' in quotes if they're missing
if [[ ! "$val" =~ $regex_contains_wrapped_quotes ]]; then
val="\"$val\""
fi
if [[ "$val" =~ $regex_contains_inside_quotes ]]; then
local tmp
tmp="${val##\"}" # Strip left quote
tmp="${tmp%%\"}" # Strip right quote
tmp="${tmp//\"/\\\"}" # Escape all quotes
val="\"$tmp\"" # Add back the wrapped quotes
unset tmp
fi
local declaration
declaration="$(printf '%b\n' "$name=$val")"
[[ "$2" == "false" ]] || eval "$declaration"
printf '%s\n' "$declaration"
done < "$1"
}
parse_ini "$@"
@egladman
Copy link
Author

MIT Licensed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment