Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading value from an ini style file with sed/awk

Tags:

bash

sed

awk

ini

I wrote a simple bash function which would read value from an ini file (defined by variable CONF_FILE) and output it

getConfValue() {
 #getConfValue section variable
 #return value of a specific variable from given section of a conf file
 section=$1
 var="$2"
 val=$(sed -nr "/\[$section\]/,/\[/{/$var/p}" $CONF_FILE)
 val=${val#$var=}
 echo "$val"
}

The problem is that it does not ignore comments and runs into troubles if multiple variables within a section names share common substrings.

Example ini file:

[general]
# TEST=old
; TEST=comment  
TEST=new
TESTING=this will be output too
PATH=/tmp/test

Running getConfValue general PATH would output /tmp/test as expected, but running getConfValue general TEST shows all the problems this approach has.

How to fix that?

I know there are dedicated tools like crudini or tools for python, perl and php out in the wild, but I do not want to add extra dependencies for simple config file parsing. A solution incorporating awk instead of sed would be just fine too.

like image 832
smihael Avatar asked Mar 12 '23 23:03

smihael


1 Answers

Sticking with sed you could anchor your var search to the start of the record using ^ and end it with an equal sign:

"/\[$section\]/,/\[/{/^$var=/p}"

If you are concerned about whitespace in front of your record you could account for that:

"/\[$section\]/,/\[/{/^(\W|)$var=/p}"

That ^(\W|)$var= says "if there is whitespace at the beginning (^(\W) or nothing (|)) before your variable concatenated with an equal sign ($var=)."

If you wanted to switch over to awk you could use something like:

val=$(awk -F"=" -v section=$section -v var=$var '$1=="["section"]"{secFound=1}secFound==1 && $1==var{print $2; secFound=0}' $CONF_FILE)

That awk command splits the record by equal -F"=". Then if the first field in the record is your section ($1=="["section"]") then set variable secFound to 1. Then... if secFound is 1 and the first field is exactly equal to your var variable (secFound==1 && $1==var) then print out the second field ({print $2}) and sets secFound to 0 so we don't pick up any other Test keys.

like image 165
JNevill Avatar answered Mar 14 '23 11:03

JNevill