Jimmy Jimmy - 1 year ago 69
Bash Question

Bash script to replace or append

I'm new to bash scripting and I'm having a bit of a hard time. I'm trying to alter the configuration values of a config file. If it finds an existing value I want it to update it, but if it doesn't exist I want it to append it. This is as far I as I got from various tutorials and snippets online:

# $1 File
# $2 Find
# $3 Replace / Append
function replaceappend() {
grep -q '^$2' $1
sed -i 's/^$2.*/$3/' $1
echo '$3' >> $1

replaceappend "/etc/test.conf" "Port 20" "Port 10"

However as you might imagine this doesn't work. It seems to be with the logic behind it, I'm not sure how to capture the result of grep in order to choose either sed or echo. Any help to get this working would be appreciated please.

Answer Source

Just use the return value of the command and use double-quotes instead of single quotes:

if ! sed -i "/$2/{s//$3/;h};"'${x;/./{x;q0};x;q1}' $1
    echo "$3" >> $1

SOURCE: Return code of sed for no match for the q command

This is treading outside my normal use of sed, so let me give an explanation of how this works, as I understand it:

sed "/$2/{s//$3/;h};"'${x;/./{x;q0};x;q1}' $1

The first /$2/ is an address - we will do the commands within {...} for any lines that match this. As a by-product it also sets the pattern-space to $2.

The command {s//$3/;h} says to substitute whatever is in the pattern-space with $3 and then save the pattern-space in the "hold-space", a type of buffer within sed.

The $ after the single quote is another address - it says to do this next command on the LAST line.

The command {x;/./{x;q0};x;q1} says:

  • x = swap the hold-space and the pattern-space
  • /./ = an address which matches anything
  • {x;q0} = swap the hold-space and the pattern-space - if this is successful (there was something in the hold-space) then q0=exit with 0 status (success)
  • x;q1 = swap the hold-space and the pattern-space - since this is now successful (due to the previous x) then q1=exit with 1 status (fail)

The double-quotes around the first part allow substitution for $2 and $3. The single quotes around the latter part prevents erroneous substitution for the $.

A bit complicated, but it seems to work AS LONG AS YOU HAVE SOMETHING IN THE FILE. An empty file will still succeed since you don't get any match on the last line.

To be honest, after all this complication... Unless the files you are working with are really long so that a double-pass would be really bad I would probably go back to the grep solution like this:

if grep -q "^$2" $1
    sed -i "s/^$2.*$/$3/" $1
    echo "$3" >>$1

That's a WHOLE lot easier to understand and maintain later...