iEFdev

Code, Computers & Random Junk

Adding Config Files to Your Bash Script

I was writing a bash script the other day, and wanted to include the option to use custom config files. Perhaps a bit overkill, but just doing it - as a test. The risky part is when a user adds their own settings is the input. So, it’s important to check/validate what they’ve added, and then of course - selecting the custom variable or the default one. So, here’s an example simular to what I did.

Varibles

First I added a section with empty variables… to show which ones are OK to edit and put in a config file.

# These settings can be set either here or used in a custom
# config file: /etc/fooBarrc, ~/.config/fooBar/fooBarrc, ~/.fooBarrc
FOO='';         # Default: ~/fooBar
BAR='';         # Default: ~/fooBar/baz_files
BAZ='';         # Default: ~/fooBar/baz_files/example.txt
FOO_NR='';      # Default: 12345

You want to do this as early as possible so any variable you dont won’t to be changed is set before and can be used later on if they add them as well.

Loading the files

Then load the files, and load them in the order you want. Last file will override the previous one. I’m just using the classic std paths where to expect config files to live.

/etc for a global alternativ, and ~/.config, ~/ for the local (user) files. All variables are empty so they can set it in the script file, or move around in the other files.

# Look for config file(s) in: /etc/fooBarrc, ~/.config/fooBar/fooBarrc, ~/.fooBarrc
# Last one has final say.
for c in /etc/fooBarrc ${HOME}/.{config/fooBar/,}$fooBarrc; do
    [ -f ${c} ] && . "${c}";
done;

With a small checking for the file to exist.

Validation, validation, validation…

With external files sourced right into your script - the user input could be just anything. So, make sure to validate everything.

In the example above, there are 4 variables: 2 folders, 1 file and a number. For that, the validation would look something like this:

[ -d ${FOO} ] && FOO=${FOO} || FOO=;
[ -d ${BAR} ] && BAR=${BAR} || BAR=;
[ -f ${BAZ} ] && BAZ=${BAZ} || BAZ=;
[[ ${FOO_NR} =~ ^[0-9]+$ ]] && FOO_NR=${FOO_NR} || FOO_NR=;

-d for directory, -f for file exist, and the last one checks if it is a number. So, now they can’t add a string to anything but a directory path or to a file - and the last one can’t be nothing but a number.

If the validation checks, the variable is set, or it will return an empty variable/value. In that way you can get the script to use a variable, or a default value.

“Parameter Expansion”

To set the default values I use “parameter expansion” to get a value or using the default.

Example:

EXAMPLE=${SOME_VAR:=/my/default/value};
echo ${EXAMPLE};

If SOME_VAR is set it will display that one, or it will take the default value, after the :=.

So, for the example above…

FOO="${FOO:=${HOME}/fooBar}";
BAR="${BAR:=${FOO}/baz_files}";
BAZ="${BAZ:=${BAR}/$(date '+%F')_fooBar.txt}";
FOO_NR="${FOO_NR:=12345}";

Now you can echo ${FOO_NR} and see what happens if you change that in a file (or above) and use letters instead, for example. smile

Maybe not the perfect way/solution - but at least something that both let the user change a few settings, getting them validated and still have your defaults left alone. That is, they don’t need to hack the code/script and can use an external file instead. +1

Example

Here’s the example of all parts above put together…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env bash
#
#
# ~/ShellScripts/fooBar
#
# Description: ...
#


# These settings can be set either here or used in a custom
# config file: /etc/fooBarrc, ~/.config/fooBar/fooBarrc, ~/.fooBarrc
FOO='';         # Default: ~/fooBar
BAR='';         # Default: ~/fooBar/baz_files
BAZ='';         # Default: ~/fooBar/baz_files/fooBar.txt.txt
FOO_NR='';      # Default: 12345


# Don't edit below, unless you know what you're doing.
# ------------------------------------------------------------

# Look for config file(s) in: /etc/fooBarrc, ~/.config/fooBar/fooBarrc, ~/.fooBarrc
# Last one has final say.
for c in /etc/fooBarrc ${HOME}/.{config/fooBar/,}$fooBarrc; do
    [ -f ${c} ] && . "${c}";
done;

# Validate, or return empty vars
[ -d ${FOO} ] && FOO=${FOO} || FOO=;
[ -d ${BAR} ] && BAR=${BAR} || BAR=;
[ -f ${BAZ} ] && BAZ=${BAZ} || BAZ=;
[[ ${FOO_NR} =~ ^[0-9]+$ ]] && FOO_NR=${FOO_NR} || FOO_NR=;

# Set vars
FOO="${FOO:=${HOME}/fooBar}";
BAR="${BAR:=${FOO}/baz_files}";
BAZ="${BAZ:=${BAR}/$(date '+%F')_fooBar.txt}";
FOO_NR="${FOO_NR:=12345}";


# Test
echo ${FOO};
echo ${BAR};
echo ${BAZ};
echo ${FOO_NR};

Of course you could add the defaults to the empty vars in the validation block, but to keep the code cleaner, and a bit more verbose. I preferred to do it that way, and each of the 4 sections are doing their own thing …sort of.


Happy hacking…

/Eric

Comments