64

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 :)

IcyIcyIce
  • 1,119

6 Answers6

89

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.)

muru
  • 197,895
  • 55
  • 485
  • 740
  • That's a great way, i could use sed substitute whitespace for "=" – IcyIcyIce Mar 08 '16 at 22:52
  • There's one more thing... what if a clueless user adds rm -rf / to the script? This script could just kill the computer... any ideas? – IcyIcyIce Mar 08 '16 at 22:53
  • 2
    @IcyIcyIce Just don't. Why can't you write it with = from the start? – muru Mar 08 '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 08 '16 at 22:55
  • this is something I have to do in school. I think I'll do it with the source... It's just that they always test all the shit and I want as many points as possible. But I'll say that only priviledged users should have access to the script and try it that way. Thanks guys :) – IcyIcyIce Mar 08 '16 at 22:59
  • And there's a format I have to use. So I'll have to use sed every time I load it. But whatever – IcyIcyIce Mar 08 '16 at 23:00
  • 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 08 '16 at 23:00
  • 1
    @IcyIcyIce see update. – muru Mar 08 '16 at 23:09
  • That one is really nice – IcyIcyIce Mar 08 '16 at 23:25
  • For anyone wanting to do this and aren't 100% sure there will be a file but don't want the error to show, redirect the error using . /path/to/some.config 2>/dev/null – Liam Pillay Mar 04 '22 at 12:32
  • When you source a file, you are executing whatever commands exists in it. It may be a security risk, so be aware of that. – Fernando Fabreti Nov 10 '23 at 17:39
10

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.

muru
  • 197,895
  • 55
  • 485
  • 740
mbiber
  • 840
  • 5
  • 12
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

Jacob Vlijm
  • 83,767
  • 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 08 '16 at 20:40
3

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!

smARTin
  • 41
  • 4
    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
  • 2
    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
  • 1
    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
  • 1
    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
2
#------------------------------------------------------------------------------
# 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
Yordan Georgiev
  • 261
  • 2
  • 6
0

one more option is to use ini format and manage config with crudini.
install crudini on Ubuntu: sudo apt install crudini
usage example:

$ crudini --set example.ini "section name" "value name" "foo bar"
$ cat example.ini
[section name]
value name = foo bar
$ value_name=$(crudini --get example.ini "section name" "value name")
$ echo "$value_name"
foo bar
$

i would avoid source in my scripts cause it will execute arbitrary code.

source.config

var1=val1; echo rm -rf /

sourcing this file will run echo rm -rf /

atti
  • 167