The question is published on by Tutorial Guruji team.
I have looked at a couple bash ini parsing scripts and I’ve seen this one used a few times here so I’m trying to see if it will work for me. It looks like it reads the ini file line by line multiple times and with each pass it progressively constructs a function that finally gets eval’d. It works fine for some special characters but not others. If a value in the file contains a single quote or greater/less than symbol, the script returns syntax errors. Other symbols create unexpected results as well. How can I handle these characters as the are encountered?
This is the function that parses the ini.
#!/usr/bin/env bash cfg_parser () { ini="$(<$1)" # read the file ini="${ini//[/[}" # escape [ ini="${ini//]/]}" # escape ] IFS=$'n' && ini=( ${ini} ) # convert to line-array ini=( ${ini[*]//;*/} ) # remove comments with ; ini=( ${ini[*]/ =/=} ) # remove tabs before = ini=( ${ini[*]/= /=} ) # remove tabs be = ini=( ${ini[*]/ = /=} ) # remove anything with a space around = ini=( ${ini[*]/#\[/}$'n'cfg.section.} ) # set section prefix ini=( ${ini[*]/%\]/ (} ) # convert text2function (1) ini=( ${ini[*]/=/=( } ) # convert item to array ini=( ${ini[*]/%/ )} ) # close array parenthesis ini=( ${ini[*]/%\ )/ \} ) # the multiline trick ini=( ${ini[*]/%( )/() {} ) # convert text2function (2) ini=( ${ini[*]/%} )/}} ) # remove extra parenthesis ini[0]="" # remove first element ini[${#ini[*]} + 1]='}' # add the last brace eval "$(echo "${ini[*]}")" # eval the result }
ini file
[Section1] value1=abc`def # unexpected EOF while looking for matching ``' value2=ghi>jkl # syntax error near unexpected token `>' value3=mno$pqr # executes ok but outputs "mnoqr" value4=stu;vwx # executes ok but outputs "stu"
Answer
The fact that you can do something in bash
doesn’t mean that you should.
sh
(and bash
etc) scripts are best suited to be relatively simple wrappers to launch programs or around text-processing commands. For more complicated tasks, including parsing ini files and acting on them, other languages are more appropriate. Have you considered writing your script in perl
or python
? Both have good .ini file parsers – I’ve used perl’s Config::INI
module several times when I’ve needed to parse an ini file.
But if you insist on doing it in bash, you should use an associative array instead of setting individual variables.
Start with something like this:
#! /bin/bash inifile='user1074170.ini' # declare $config to be an associative array declare -A config while IFS='=' read -r key val ; do config["$key"]="$val" done < <(sed -E -e '/^[/d s/#.*// s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" ) # now print out the config array set | grep '^config='
The sed
script deletes the [Section1]
line (actually, all lines beginning with an open-square-bracket [
– you will want to handle this differently[1] in an ini file with multiple sections), and removes comments as well as leading and trailing blanks. The while
loop reads in each line, using =
as a field delimiter, and assigns the contents to variables $key and $val, which are then added to the $config array.
Output:
config=([value1]="abc`def" [value3]="mno$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )
You can use the array entries later in your script like this:
$ echo value1 is "${config[value1]}" value1 is abc`def $ [ "${config[value4]}" = 'stu;vwx' ] && echo true true
[1] awk
or perl
have conveniently easy ways of reading files in “paragraph” mode. A paragraph being defined as a block of text separated from other text blocks by one or more blank lines.
e.g. to work with only [Section1]
, insert the awk
script below immediately before the sed
script feeding into the while
loop above:
awk -v RS= -v ORS='nn' '/[Section1]/' "$inifile" | sed ...
(and remove "$inifile"
from the end of the sed
command line, of course – you don’t want to feed the file in again after you’ve gone to the trouble of extracting only [Section1]
from it).
Setting ORS
isn’t strictly necessary if you’re only extracting one section from the ini file – but will be useful to maintain paragraph separation if you’re extracting two or more sections.