Bash itself doesn’t have a built-in configuration file parser like Python’s configparser or Go’s viper.
Let’s say you have a config file like this:
# My application settings
database_host=localhost
database_port=5432
api_key=abc123xyz
feature_flags=true,false,true
user_list="alice","bob","charlie"
And you want to load these into Bash variables. You could just source the file if it’s valid Bash syntax:
source my_app.conf
echo $database_host
echo $api_key
But that breaks if your config file has comments, or values that Bash might misinterpret (like true,false,true being treated as a command). It’s also brittle if the config format changes slightly.
The most robust, idiomatic way is to use grep, sed, or awk to extract values. For simple key-value pairs, grep and sed are often sufficient and easier to grasp.
Here’s a common pattern using grep and sed to parse that my_app.conf file:
#!/bin/bash
CONFIG_FILE="my_app.conf"
# Read each line, skip comments and empty lines
while IFS= read -r line; do
# Skip comments and empty lines
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
# Extract key and value
# Use sed to remove leading/trailing whitespace and the '=' sign
key=$(echo "$line" | sed -e 's/=.*//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
value=$(echo "$line" | sed -e 's/^[^=]*=//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
# Handle quoted values by removing them
value=$(echo "$value" | sed -e 's/^"//' -e 's/"$//')
# Assign to a variable, using declare for safety and to handle potential special characters
declare "$key=$value"
done < <(grep -v '^[[:space:]]*#' "$CONFIG_FILE" | grep -v '^[[:space:]]*$')
# Now you can use the variables
echo "Database Host: $database_host"
echo "Database Port: $database_port"
echo "API Key: $api_key"
echo "Feature Flags: $feature_flags"
echo "User List: $user_list"
Let’s break down what’s happening there. The while IFS= read -r line loop reads the file line by line. IFS= prevents leading/trailing whitespace from being stripped by read, and -r prevents backslash escapes from being interpreted.
The done < <(...) part is process substitution. It runs the grep commands first, and their output becomes the input to the while loop. grep -v '^[[:space:]]*#' filters out lines starting with a # (optionally preceded by whitespace), and grep -v '^[[:space:]]*$' filters out blank lines (also allowing for leading whitespace). This gives us clean lines to process.
Inside the loop, sed is doing the heavy lifting for parsing each line.
sed -e 's/=.*//': This takes everything from the first=onwards and removes it, leaving just the key.sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//': These two-eexpressions strip any leading or trailing whitespace from the key.sed -e 's/^[^=]*=//': This removes everything up to and including the first=, leaving just the value.sed -e 's/^"//' -e 's/"$//': This specifically handles values enclosed in double quotes, stripping the leading and trailing quotes.
Finally, declare "$key=$value" is crucial. It dynamically creates a variable with the name stored in $key and assigns it the value from $value. Using declare is safer than eval or direct assignment because it handles variable names that might contain special characters or look like Bash commands more predictably.
This approach is flexible enough to handle variations like:
- Values with spaces (if not quoted):
my_setting=some value here - Different delimiters (though
=is standard): you’d adjust thesedcommands. - Multi-line values: these would require a more complex parser, often involving state tracking within the loop.
If your config file has values that should be interpreted as arrays or lists, like feature_flags=true,false,true, you’ll need to process that string further after it’s loaded into a variable. Bash doesn’t have native arrays like other languages, but you can simulate them using indexed variables or by splitting the string into an array using read -r -a:
# Assuming feature_flags="true,false,true" is already loaded
IFS=',' read -r -a flags_array <<< "$feature_flags"
echo "First flag: ${flags_array[0]}"
echo "Second flag: ${flags_array[1]}"
The <<< is a here-string, feeding the value of $feature_flags as standard input to the read command. IFS=',' tells read to split the input on commas.
The trickiest part about parsing configuration files in Bash is often handling edge cases and ensuring the parsed values are safe to use. The declare command and careful sed filtering help a lot here. Most people try to eval or just do simple string manipulation, which fails when values contain spaces, quotes, or shell metacharacters.
Once you’ve successfully parsed your configuration, the next common hurdle is dealing with complex data structures like nested configurations or conditional logic within the config itself, which Bash is poorly suited for.