If you have Git available and are OK with the constraint of not being able to use underscores in the key names, you can use git config
as a general-purpose INI parser / editor.
It will handle parsing out the key/value pair from around the =
and discard insignificant whitespace, plus you get comments (both ;
and #
) and type coercion basically for free. I have included a complete working example for the OP's input .ini
and desired output (Bash associative arrays), below.
However, given a config file like this
; mytool.ini
[section1]
inputdir = ~/some/dir
enablesomefeature = true
enablesomeotherfeature = yes
greeting = Bonjour, Monde!
[section2]
anothersetting = 42
…provided you just need a quick-and-dirty solution, and aren't married to the idea of having the settings in a Bash associative array, you could get away with as little as:
eval $(git config -f mytool.ini --list | tr . _)
# or if 'eval' skeeves you out excessively
source <(git config -f mytool.ini --list | tr . _)
which creates environment variables named sectionname_variablename
in the current environment. This, of course, only works if you can trust that none of your values will ever contain a period or whitespace (see below for a more robust solution).
Other simple examples
Fetching arbitrary values, using a shell function to save typing:
function myini() { git config -f mytool.ini; }
An alias would be OK, here, too, but those are not normally expanded in a shell script [1], and anyway aliases are superseded by shell functions "for almost every purpose," [2], according to the Bash man page.
myini --list
# result:
# section1.inputdir=~/some/dir
# section1.enablesomefeature=true
# section1.enablesomeotherfeature=yes
# section2.anothersetting=42
myini --get section1.inputdir
# result:
# ~/some/dir
With the --type
option, you can "canonicalize" specific settings as integers, booleans, or paths (automatically expanding ~
):
myini --get --type=path section1.inputdir # value '~/some/dir'
# result:
# /home/myuser/some/dir
myini --get --type=bool section1.enablesomeotherfeature # value 'yes'
# result:
# true
Slightly more robust quick-and-dirty example
Make all variables in mytool.ini
available as SECTIONNAME_VARIABLENAME
in the current environment, preserving internal whitespace in key values:
source <(
git config -f mytool.ini --list \
| sed 's/\([^.]*\)\.\(.*\)=\(.*\)/\U\1_\2\E="\3"/'
)
What the sed expression is doing, in English, is
- finding a bunch of non-period characters up to a period, remembering that as
\1
, then
- finding a bunch of characters up to an equals sign, remembering that as
\2
, and
- finding all the characters after the equals sign as
\3
- finally, in the replacement string
- the section name + variable name is upper-cased, and
- the value part is double-quoted, in case it contains characters that have special meaning to the shell if unquoted (like whitespace)
The \U
and \E
sequences in the replacement string (which upper-case that part of the replacement string) are GNU sed
extension. On macOS and BSD, you'd just use multiple -e
expressions to achieve the same effect.
Dealing with embedded quotes and whitespace in the section names (which git config
allows) is left as an exercise for the reader. :)
Using section names as keys into a Bash associative array
Given:
; foo.ini
[foobar]
session=foo
path=/some/path
[barfoo]
session=bar
path=/some/path
This will produce the result the OP is asking for, simply by rearranging some of the captures in the sed replacement expression, and will work fine without GNU sed:
source <(
git config -f foo.ini --list \
| sed 's/\([^.]*\)\.\(.*\)=\(.*\)/declare -A \2["\1"]="\3"/'
)
I predict there could be some challenges with quoting for a real-world .ini
file, but it works for the provided example. Result:
declare -p {session,path}
# result:
# declare -A session=([barfoo]="bar" [foobar]="foo" )
# declare -A path=([barfoo]="/some/path" [foobar]="/some/path" )