21

What is the best way to read a config file in bash?
For example, you have a script and aren't willing to fill in all the config manually each time you call the script.

Edit 1: I think I didn't make it clear, so: what I want is... I have a configfile which is like

variable_name value  
variable_name value ...  

and I want to read that. I know I could just grep it for the arguments I'm looking for or so... but maybe there's a more intelligent way :)

  • 2
    You'll have to show what kind of configuration you mean. Provide an example. – muru Mar 8 '16 at 16:25
  • See also a very similar question on the Unix & Linux StackExchange. – Michael Dec 12 '18 at 21:03
31

As mbiber said, source another file. For example, your config file (say some.config) would be:

var1=val1
var2=val2

And your script could look like:

#! /bin/bash

# Optionally, set default values
# var1="default value for var1"
# var1="default value for var2"

. /path/to/some.config

echo "$var1" "$var2"

The many files in /etc/default usually serve as configuration files for other shell scripts in a similar way. A very common example from posts here is /etc/default/grub. This file is used to set configuration options for GRUB, since grub-mkconfig is a shell script that sources it:

sysconfdir="/etc"
#…
if test -f ${sysconfdir}/default/grub ; then
  . ${sysconfdir}/default/grub
fi

If you really must process configuration of the form:

var1 some value 1
var2 some value 2

Then you could do something like:

while read var value
do
    export "$var"="$value"
done < /path/to/some.config

(You could also do something like eval "$var=$value", but that's riskier than sourcing a script. You could inadvertently break that more easily than a sourced file.)

  • That's a great way, i could use sed substitute whitespace for "=" – IcyIcyIce Mar 8 '16 at 22:52
  • 1
    @IcyIcyIce Just don't. Why can't you write it with = from the start? – muru Mar 8 '16 at 22:53
  • 1
    @IcyIcyIce That's how it's supposed to be. You can use shell code in the various files in /etc/default - and mostly to good use. If your clueless user has access to configuration for something running as root, it's already a lost cause. – muru Mar 8 '16 at 22:55
  • 1
    @IcyIcyIce get your teachers to clarify what they want. A part of learning to develop software is getting to know the requirements clearly. – muru Mar 8 '16 at 23:00
  • 1
    @IcyIcyIce see update. – muru Mar 8 '16 at 23:09
4

Obviously, I am not the bash specialist here, but the concept should not be different in whatever language you use:

An example

In the example below, you can use a (very) basic script to either set a string, or print a string, as set in your config file:

#!/bin/bash

# argument to set a new string or print the set string
arg=$1
# possible string as second argument
string=$2
# path to your config file
configfile="$HOME/stringfile"

# if the argument is: "set", write the string (second argument) to a file
if [ "$arg" == "set" ]
then
echo "$string" > $configfile 
# if the argunment is "print": print out the set string, as defined in your file
elif [ "$arg" == "print" ]
then 
echo "$( cat $configfile )"
fi

Then

  • To set a string into your config file:

    $ '/home/jacob/Bureaublad/test.sh' set "Een aap op een fiets, hoe vind je zoiets?"
    
  • Subsequently, to print out the string, as defined in your "configfile":

    $ '/home/jacob/Bureaublad/test.sh' print
    Een aap op een fiets, hoe vind je zoiets?
    

Of course, in a real applied script, you need to add a lot of stuff to make sure the arguments are correct, decide what to do when input is incorrect, settings file does not exist etc, but:

This is the basic idea

  • thanks, i'm just updating the question i think i didnt make it clear... but in simple cases this would work like a charm ofc – IcyIcyIce Mar 8 '16 at 20:40
3

Use source or . to load in a file.

source /path/to/file

or

. /path/to/file

It's also recommended to check if the file exists before loading it because you don't want to continue running your script if a configuration file is not present.

0

I am using this one...

#!/bin/bash

CFG_FILE=/etc/test.conf
CFG_CONTENT=$(cat $CFG_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g')
eval "$CFG_CONTENT"

Conf file is parsed with sed and then evaluated as simple variable asignments

First sed parsing key values (supports also = surrounded with spaces)

Second sed removes spaces around = sign for valid variable asignment

Futher sed treatment can be added

All other not matching text in conf file will be removed (including # or ; commentary and other)

Be aware that single line shell commands can be also evaluated from this config file!

  • 1
    Useless use of cat. Why do you invoke sed twice back-to-back? You can chain sed commands like so: sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g' – David Foerster Feb 24 '17 at 15:16
  • Also, this sed command will generate an empty output for the sample configuration file entries in the question. -1 – David Foerster Feb 24 '17 at 15:18
  • 1
    Another possible improvement: use the . command and process redirection instead of temporary variables to hold the output of the sub-process: . <(sed ... "$CFG_FILE") – David Foerster Feb 24 '17 at 15:32
  • Of course my example uses key=value like config file. It is better practise than "key value". Using key=value my example works. Your chaining of sed commands is better, I admit, but source command expects file so we can use: eval $(sed -r '/[^=]+=[^=]+/!d;s/\s+=\s/=/g' "$CFG_FILE"), which is way better to understand what there is going on. – smARTin Feb 27 '17 at 14:40
  • Yes, . expects a file and process substitution is expanded to a file. . <(echo echo foo) works just as expected in Bash v4.3.11. In small examples it probably won't matter. I'd rather not pass multiple kilobytes through command substitution though. – David Foerster Feb 27 '17 at 15:00
0
#------------------------------------------------------------------------------
# parse the ini like $0.$host_name.cnf and set the variables
# cleans the unneeded during after run-time stuff. Note the MainSection
# courtesy of : http://mark.aufflick.com/blog/2007/11/08/parsing-ini-files-with-sed
#------------------------------------------------------------------------------
doParseConfFile(){
    # set a default cnfiguration file
    cnf_file="$run_unit_bash_dir/$run_unit.cnf"

    # however if there is a host dependant cnf file override it
    test -f "$run_unit_bash_dir/$run_unit.$host_name.cnf" \
    && cnf_file="$run_unit_bash_dir/$run_unit.$host_name.cnf"

    # yet finally override if passed as argument to this function
    # if the the ini file is not passed define the default host independant ini file
    test -z "$1" || cnf_file=$1;shift 1;


    test -z "$2" || ini_section=$2;shift 1;
    doLog "DEBUG read configuration file : $cnf_file"
    doLog "INFO read [$ini_section] section from config file"

    # debug echo "@doParseConfFile cnf_file:: $cnf_file"
    # coud be later on parametrized ...
    test -z "$ini_section" && ini_section='MAIN_SETTINGS'

    doLog "DEBUG reading: the following configuration file"
    doLog "DEBUG ""$cnf_file"
    ( set -o posix ; set ) | sort >"$tmp_dir/vars.before"

    eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
        -e 's/#.*$//' \
        -e 's/[[:space:]]*$//' \
        -e 's/^[[:space:]]*//' \
        -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
        < $cnf_file \
        | sed -n -e "/^\[$ini_section\]/,/^\s*\[/{/^[^#].*\=.*/p;}"`

    ( set -o posix ; set ) | sort >"$tmp_dir/vars.after"

    doLog "INFO added the following vars from section: [$ini_section]"
    cmd="$(comm -3 $tmp_dir/vars.before $tmp_dir/vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
    echo -e "$cmd"
    echo -e "$cmd" >> $log_file
    echo -e "\n\n"
    sleep 1; printf "\033[2J";printf "\033[0;0H" # and clear the screen
}
#eof func doParseConfFile

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.