59

I'm writing a bash script to modify a config file which contains a bunch of key/value pairs. How can I read the key and find the value and possibly modify it?

1

8 Answers 8

122

A wild stab in the dark for modifying a single value:

sed -c -i "s/\($TARGET_KEY *= *\).*/\1$REPLACEMENT_VALUE/" $CONFIG_FILE

assuming that the target key and replacement value don't contain any special regex characters, and that your key-value separator is "=". Note, the -c option is system dependent and you may need to omit it for sed to execute.

For other tips on how to do similar replacements (e.g., when the REPLACEMENT_VALUE has '/' characters in it), there are some great examples here.

Sign up to request clarification or add additional context in comments.

8 Comments

-c seems to be invalid in my environment. When I get rid of -c, it works. Thanks.
@prolink: ssh [opts] [user@]hostname command will log in then run the given command. If the command is complex enough, you might want to store it in a script on the remote so you can just run ssh <host> path/to/script.
If anyone perform it on a Mac environment, you might want to remove -c and add '' after -i. just like sed -i '' "s...." $CONFIG_FILE
Very useful script. If all your keys start at the beginning of the lines, consider adding ^ before $TARGET_KEY, otherwise if you have two keys like even=2 and noteven=1 you'll end up changing both.
How about adding the value in case it doesn't exist previously?
|
23

Hope this helps someone. I created a self contained script, which required config processing of sorts.

#!/bin/bash
CONFIG="/tmp/test.cfg"

# Use this to set the new config value, needs 2 parameters. 
# You could check that $1 and $2 is set, but I am lazy
function set_config(){
    sudo sed -i "s/^\($1\s*=\s*\).*\$/\1$2/" $CONFIG
}

# INITIALIZE CONFIG IF IT'S MISSING
if [ ! -e "${CONFIG}" ] ; then
    # Set default variable value
    sudo touch $CONFIG
    echo "myname=\"Test\"" | sudo tee --append $CONFIG
fi

# LOAD THE CONFIG FILE
source $CONFIG

echo "${myname}" # SHOULD OUTPUT DEFAULT (test) ON FIRST RUN
myname="Erl"
echo "${myname}" # SHOULD OUTPUT Erl
set_config myname $myname # SETS THE NEW VALUE

1 Comment

I doubt if this works with TOML, or even YAML or JSON, as they have sections. It works fine for just key-value pairs though
5

Assuming that you have a file of key=value pairs, potentially with spaces around the =, you can delete, modify in-place or append key-value pairs at will using awk even if the keys or values contain special regex sequences:

# Using awk to delete, modify or append keys
# In case of an error the original configuration file is left intact
# Also leaves a timestamped backup copy (omit the cp -p if none is required)
CONFIG_FILE=file.conf
cp -p "$CONFIG_FILE" "$CONFIG_FILE.orig.`date \"+%Y%m%d_%H%M%S\"`" &&
awk -F '[ \t]*=[ \t]*' '$1=="keytodelete" { next } $1=="keytomodify" { print "keytomodify=newvalue" ; next } { print } END { print "keytoappend=value" }' "$CONFIG_FILE" >"$CONFIG_FILE~" &&
mv "$CONFIG_FILE~" "$CONFIG_FILE" ||
echo "an error has occurred (permissions? disk space?)"

Comments

4
sed "/^$old/s/\(.[^=]*\)\([ \t]*=[ \t]*\)\(.[^=]*\)/\1\2$replace/" configfile

Comments

3

So I can not take any credit for this as it is a combination of stackoverflow answers and help from irc.freenode.net #bash channel but here are bash functions now to both set and read config file values:

# https://stackoverflow.com/a/2464883
# Usage: config_set filename key value
function config_set() {
  local file=$1
  local key=$2
  local val=${@:3}

  ensureConfigFileExists "${file}"

  # create key if not exists
  if ! grep -q "^${key}=" ${file}; then
    # insert a newline just in case the file does not end with one
    printf "\n${key}=" >> ${file}
  fi

  chc "$file" "$key" "$val"
}

function ensureConfigFileExists() {
  if [ ! -e "$1" ] ; then
    if [ -e "$1.example" ]; then
      cp "$1.example" "$1";
    else
      touch "$1"
    fi
  fi
}

# thanks to ixz in #bash on irc.freenode.net
function chc() { gawk -v OFS== -v FS== -e 'BEGIN { ARGC = 1 } $1 == ARGV[2] { print ARGV[4] ? ARGV[4] : $1, ARGV[3]; next } 1' "$@" <"$1" >"$1.1"; mv "$1"{.1,}; }

# https://unix.stackexchange.com/a/331965/312709
# Usage: local myvar="$(config_get myvar)"
function config_get() {
    val="$(config_read_file ${CONFIG_FILE} "${1}")";
    if [ "${val}" = "__UNDEFINED__" ]; then
        val="$(config_read_file ${CONFIG_FILE}.example "${1}")";
    fi
    printf -- "%s" "${val}";
}
function config_read_file() {
    (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}

at first I was using the accepted answer's sed solution: https://stackoverflow.com/a/2464883/2683059

however if the value has a / char it breaks

Comments

0

in general it's easy to extract the info with grep and cut:


cat "$FILE" | grep "^${KEY}${DELIMITER}" | cut -f2- -d"$DELIMITER"

to update you could do something like this:


mv "$FILE" "$FILE.bak"
cat "$FILE.bak" | grep -v "^${KEY}${DELIMITER}" > "$FILE"
echo "${KEY}${DELIMITER}${NEWVALUE}" >> "$FILE"

this would not maintain the order of the key-value pairs obviously. add error checking to make sure you don't lose your data.

1 Comment

if you run out of disk space or if for whatever reason you cannot create FILE then your solution will leave FILE missing (one would have to manually rename FILE.bak back to FILE to recover.)
0

I have done this:

new_port=$1
sed "s/^port=.*/port=$new_port/" "$CONFIG_FILE" > /yourPath/temp.x
mv /yourPath/temp.x "$CONFIG_FILE"

This will change port= to port=8888 in your config file if you choose 8888 as $1 for example.

Comments

-1

Suppose your config file is in below format:

CONFIG_NUM=4
CONFIG_NUM2=5
CONFIG_DEBUG=n

In your bash script, you can use:

CONFIG_FILE=your_config_file
. $CONFIG_FILE

if [ $CONFIG_DEBUG == "y" ]; then
    ......
else
    ......
fi

$CONFIG_NUM, $CONFIG_NUM2, $CONFIG_DEBUG is what you need.

After your read the values, write it back will be easy:

echo "CONFIG_DEBUG=y" >> $CONFIG_FILE

1 Comment

That won't replace the value, just add it again at the bottom, it's completely dependent on the config file being a valid bash script, and... wow, what if there's something malicious in the config file?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.