How to parse config files with Bash
Storing settings in separate files (not code) lets anyone change them easily, even without coding skills. It’s like having separate dials and switches for your programs, instead of needing a screwdriver to adjust everything.
This is part seven in a tutorial series on shell scripts. The previous tutorial taught you how to test early and often.
Intro
Keeping program configurations separate from code is important. It enables non-programmers to alter configurations without having to modify the program’s code. Even with Open Source, changing compiled programs is tough for non-programmers. It requires the original code (available) and serious programming skills, which most lack and wouldn’t want to learn.
While Bash scripts are easy to read because they’re not compiled, it’s still not recommended for non-programmers to edit them directly. Even though you can see the code, it can be tricky to understand and modifying it can lead to unexpected problems. Even knowledgeable developers and sysadmins can make accidental changes causing errors or worse.
Config files allow settings to be separated from code, making it easy for anyone to make tweaks and keeping the program safe. Remember to think of config files as dials, not deep mechanics. Many developers do this for programs written in compiled languages because they don’t expect the users to be developers. For many of the same reasons, it also makes sense to do this with interpreted shell languages.
The usual way
As with many other languages, you can write code for a Bash program that reads and parses ASCII text configuration files, reads the variable name, and sets values as the program code executes. For example, a configuration file might look like this:
var1=LinuxGeek46
var2=Opensource.com
The program would read that file, parse each line, and set the values into each variable.
Sourcing
Bash uses a much easier method for parsing and setting variables called sourcing. Sourcing an external file from an executable shell program is a simple method for including the content of that file into a shell program in its entirety. In one sense, this is very much like compiled language include statements that include library files at runtime. Such a file can include any type of Bash code, including variable assignments.
As usual, it’s easier to demonstrate than to explain.
First, create a ~/bin directory (if it does not already exist), and make it the present working directory (PWD). The Linux Filesystem Hierarchical Standard defines ~/bin as the appropriate place for users to store their executable files.
Create a new file in this directory. Name it main and make it executable:
$ touch main
$ chmod +x main
Add the following content to this executable file:
#!/bin/bash
Name="LinuxGeek"
echo $Name
And execute this Bash program:
$ ./main
LinuxGeek
Create a new file and call it ~/bin/data. This file does not need to be executable. Add the following information to it:
# Sourced code and variables
echo "This is the sourced code from the data file."
FirstName="David"
LastName="Both"
Add three lines to the main program so that it looks like this:
#!/bin/bash
Name="LinuxGeek"
echo $Name
source ~/bin/data
echo "First name: $FirstName"
echo "LastName: $LastName"
Rerun the program:
$ ./main
LinuxGeek
This is the sourced code from the data file.
First name: David
LastName: Both
There is one more cool thing to know about sourcing. You can use a single dot (.) as a shortcut for the source command. Change the main file to substitute the . in place of source:
#!/bin/bash
Name="LinuxGeek"
echo $Name
. ~/bin/data
echo "First name: $FirstName"
echo "LastName: $LastName"
And run the program again. The result should be exactly the same as the previous run.
Starting Bash
Every Linux host that uses Bash—which is pretty much all of them since Bash is the default shell for all Linux distributions—includes some excellent, built-in examples of sourcing.
Whenever a Bash shell starts, its environment must be configured so that it is usable. There are five main files and one directory that are used to configure the Bash environment. They are listed here along with their main functions:
/etc/profile: System-wide environment and startup programs/etc/bashrc: System-wide functions and aliases/etc/profile.d/: Directory that contains system-wide scripts for configuring various command-line tools such asvimandmcand any custom configuration scripts a sysadmin creates~/.bash_profile: User-specific environment and startup programs~/.bashrc: User-specific aliases and functions~/.bash_logout: User-specific commands to execute when the user logs out
Try to trace the execution sequence through these files and determine which sequence it uses for a non-login Bash initialization versus a log-in Bash initialization. I did this in Chapter 17 of Volume 1 in my Linux training series, Using and administering Linux: Zero to sysadmin.
I’ll give you one hint. It all starts with the ~/.bashrc script.
Conclusion
This tutorial explored a seemingly simple technique – parsing variables from a config file. Don’t be fooled by its simplicity: This approach offers a powerful way to separate code from settings, making programs more user-friendly and adaptable. It’s a win-win for developers and users alike.
