Comments
- Loading...
Many times a script is written that needs extra/persistent configuration from the user. In most languages this is no big deal, you just import your json/yaml/toml parser and you're good to go. The common thing to do for many bash scripts that require configuration is to ask the user to put another actual bash script containing variable declarations in the file:
# configfoo='bar baz'# script#!/usr/bin/env bash# ...source $HOME/.config/my_script/configecho "$foo" # prints 'bar baz'
But this is horrible to me. This doesn't just define variables, this evaluates code! Ideally, a configuration file should be parsed, not evaluated. For a few scripts I've written lately, I've been including a function that parses plain-text in the following format:
somekey some valueanotherkey some valueanotherkey another value# a comment
Here's how I parse config files:
#!/usr/bin/env bashdeclare -r config_dir="${XDG_CONFIG_DIR:-$HOME/.config}/my_script"declare -r config_file="${config_dir}/conf"declare somekey=foo # define a default valuedeclare -a anotherkeyparse_config_file() {local line key val nr=0local config_err=()while IFS= read -r line; do# keep a running count of which line we're on(( ++nr ))# ignore empty lines and lines starting with a #[[ -z "$line" || "$line" = '#'* ]] && continueread -r key <<< "${line%% *}" # grabs the first word and strips trailing whitespaceread -r val <<< "${line#* }" # grabs everything after the first word and strips trailing whitespaceif [[ -z "$val" ]]; then# store errors in an arrayconfig_err+=( "missing value for \"$key\" in config file on line $nr" )continueficase "$key" in # here it actually checks for keys and stores their valuessomekey)# test to see if a key matches a specific set of valuesif [[ $val =~ ^foo$|^bar$|^baz$ ]]; thensomekey="$val"else# more error handlingconfig_err+=( "unknown value \"$val\" for \"$key\" in config file on line $nr" )config_err+=( ' must be "foo" "bar" or "baz"' )fi ;;anotherkey) anotherkey+=( "$val" ) ;; # allow multiple keys stored in an array*) config_err+=( "unknown key \"$key\" in config file on line $nr" )esacdoneif (( ${#config_err[@]} > 0 )); thenprintf '%s\n' 'there were errors parsing the config file:' "${config_err[@]}"fi}[[ -s "$config_file" ]] && parse_config_file < "$config_file"printf 'somekey is "%s"\n' "$somekey"printf 'anotherkey contains "%s"\n' "${anotherkey[*]}"
Hopefully the comments explain it well enough!