0

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:

# FUNCTION TO MODIFY CONFIG BY APPEND OR REPLACE
# $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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Jimmy
  • 12,087
  • 28
  • 102
  • 192

1 Answers1

3

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
then
    echo "$3" >> $1
fi

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
then
    sed -i "s/^$2.*$/$3/" $1
else
    echo "$3" >>$1
fi

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

Community
  • 1
  • 1
Peter Bowers
  • 3,063
  • 1
  • 10
  • 18
  • 1
    You could mess with the `grep` if you wanted and then do the `sed` in an else, but easier/faster to just use the return value of `sed` directly. – Peter Bowers Mar 15 '15 at 12:54
  • Whilst I agree to keeping it in sed, sed will return the same regardless of whether you find the value or not **I think**. For that I think you need to use the ```q``` flag but i dont understand how to use it – Jimmy Mar 15 '15 at 13:00
  • Thank you for your extremely comprehensive answer – Jimmy Mar 15 '15 at 14:06