patternbashMinor
Reading options from .cfg file
Viewed 0 times
readingfileoptionscfgfrom
Problem
I have a bash script that reads a config file with key/value pairs and uses these values to modify the functioning of other scripts correspondingly.
Example .cfg file:
The file consists of a very less amount of lines currently but there might be a chance of increase in the size of the file in the future.
I have written code that reads the file line by line and gets the key value pairs. Is it better (in terms of performance such as speed, memory requirements and coding standards) to code this way or to search for the key strings and find its corresponding value using grep?
TCPATH=$(echo $SCRIPTPATH | sed 's/\/bld.*//')
dir=$TCPATH/bld/code_compliance
# Define the colours used for interface output.
source $TCPATH/bld/common/def_colours
echo "VF Code Compliance Tests: $(date)" > $3
echo "" >> $3
while read line2
do
name2=$line2
name2=${name2^^}
if [[ $name2 =~ "=" && $name2 =~ "OUTPUT" ]]; then
option2=${name2%=*}
value2=${name2#*=}
start2=${value2#*\'}
end2=${start2%\'*}
output=$end2
fi
done > $3 2> /dev/null
sed -r -i "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" $3
;;
CLASSSTRUCTNAME*)
bash $dir/classnamecheck $1 >
Example .cfg file:
layout='true'
code_compliance='true'
....
....
variablename='false'
output='true'
The file consists of a very less amount of lines currently but there might be a chance of increase in the size of the file in the future.
I have written code that reads the file line by line and gets the key value pairs. Is it better (in terms of performance such as speed, memory requirements and coding standards) to code this way or to search for the key strings and find its corresponding value using grep?
#!/bin/bash
###########################################################################################################################
#This script is responsible for reading inputs from the configuration file and running corresponding scripts.
#If the value for an option is set to true, then the suitable script is run.
#If the user provides an invalid value for an option, an error message is displayed.
#Likewise, if an invalid option is provided, an error message is displayed.
###########################################################################################################################
SCRIPT=readlink -f $0
SCRIPTPATH=dirname $SCRIPT`TCPATH=$(echo $SCRIPTPATH | sed 's/\/bld.*//')
dir=$TCPATH/bld/code_compliance
# Define the colours used for interface output.
source $TCPATH/bld/common/def_colours
echo "VF Code Compliance Tests: $(date)" > $3
echo "" >> $3
while read line2
do
name2=$line2
name2=${name2^^}
if [[ $name2 =~ "=" && $name2 =~ "OUTPUT" ]]; then
option2=${name2%=*}
value2=${name2#*=}
start2=${value2#*\'}
end2=${start2%\'*}
output=$end2
fi
done > $3 2> /dev/null
sed -r -i "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" $3
;;
CLASSSTRUCTNAME*)
bash $dir/classnamecheck $1 >
Solution
While I don't believe that
Here's an example:
In the first place it's important to remember that
That said, I think, based on the next bit there, you could do with a little field splitting here:
Of course, such a thing depends on 1
Ok, so, all that said, you can go a long way toward doing this correctly with something like:
...though that still has several problems, it gets a list of all varnames pulled out of config.cfg and puts them in
...it saves the hardquotes into the value literally. It is better to leave the shell quotes out of the config file though - let the values be literal there - in that way it will be easy to handle...
...so you needn't rely on your users to do:
...because a great many of them will likely not do that correctly.
. sourcing a user-edited configuration file is a good way to go - executing arbitrary code is usually a bad idea - I do wholeheartedly agree with the other answer here that you have overengineered this solution.Here's an example:
...
while read line
do
name=$line
name=${name^^}
if [[ $name =~ "=" ]]; then
option=${name%=*}
value=${name#*=}
start=${value#*\'}
end=${start%\'*}
...In the first place it's important to remember that
read line does not get a line of input into the shell variable $line but rather an interpretation of that line of input is stored in $line. By default any leading or trailing whitespace is trimmed from the input and \\backslash escapes are interpreted - regardless of any 'single- or "double-quote characters. To reliably put a line of input into $line with the shell builtin read you need to do:... IFS= read -r line ...That said, I think, based on the next bit there, you could do with a little field splitting here:
... IFS== read -r name val ...Of course, such a thing depends on 1
$name + $val pair per line and it still doesn't validate $name. A portable shell name can consist of only alphanumeric characters and/or a _ character and cannot begin with a numeric character. What's more, even if you verify your $name to within that range - you must still ensure it is not a $name that your currently running shell already depends upon. If you uppercase path= with ${name^^}, for example, to PATH and then set that variable you're going to have a bad time - or, worse, your user will. Consider prepending a _ to the beginning of every var name set as a result of evaling your cfg.Ok, so, all that said, you can go a long way toward doing this correctly with something like:
set -f; IFS='
'; for v in $( sed "/^[_[:alpha:]][_[:alnum:]]*=/!d
s/'"'/&\\&&/g ; s/=/&'"'/
s/.*/_&'/" config.cfg
); do set "${v%%=*}" "$@" && eval "$v"
done; set +f...though that still has several problems, it gets a list of all varnames pulled out of config.cfg and puts them in
"$@". It safely evals them and sets the variables as well - each var= modified to a _var= - and safely handles all quoting. It handles only a single line per var value though, and if your file looks like:var='value'...it saves the hardquotes into the value literally. It is better to leave the shell quotes out of the config file though - let the values be literal there - in that way it will be easy to handle...
var=somebody's value...so you needn't rely on your users to do:
var='somebody'\''s value'...because a great many of them will likely not do that correctly.
Code Snippets
...
while read line
do
name=$line
name=${name^^}
if [[ $name =~ "=" ]]; then
option=${name%=*}
value=${name#*=}
start=${value#*\'}
end=${start%\'*}
...... IFS= read -r line ...... IFS== read -r name val ...set -f; IFS='
'; for v in $( sed "/^[_[:alpha:]][_[:alnum:]]*=/!d
s/'"'/&\\&&/g ; s/=/&'"'/
s/.*/_&'/" config.cfg
); do set "${v%%=*}" "$@" && eval "$v"
done; set +fvar='value'Context
StackExchange Code Review Q#74432, answer score: 4
Revisions (0)
No revisions yet.