views:

709

answers:

2

I have a small awk script that does some in-place file modifications (to a Java .properties file, to give you an idea). This is part of a deployment script affecting a bunch of users.

I want to be able to set defaults, leaving the rest of the file at the user's preferences. This means appending a configuration line if it is missing, modifying it if it is there, leaving everything else as it is.

Currently I use something like this:

# initialize
BEGIN {
  some_value_set      = 0
  other_value_set     = 0

  some_value_default  = "some.value=SOME VALUE"
  other_value_default = "other.value=OTHER VALUE"
}

# modify existing lines
{
  if (/^some\.value=.*/) 
  {
    gsub(/.*/, some_value_default)
    some_value_set = 1
  }
  else if (/^other\.value=.*/)
  {
    gsub(/.*/, other_value_default)
    other_value_set = 1
  }
  print $0
}

# append missing lines
END {
  if (some_value_set   == 0) print some_value_default
  if (other_value_set  == 0) print other_value_default
}

Especially when the number of lines I want to control gets larger, this is increasingly cumbersome. My awk knowledge is not all that great, and the above just feels wrong - how can I streamline this?

P.S.: If possible, I'd like to stay with awk. Please don't just recommend that using Perl/Python/whatever would be much easier. :-)

+2  A: 

My AWK is rusty, so I won't provide actual code.

  • Initialize an array with the regular expressions and values.
  • For each line, iterate the array and do appropriate substitutions. Clean out used entries.
  • At end, iterate the array and append lines for remaining entries.
Darron
Basically, this is exactly what @Sean Bright has implemented. +1
Tomalak
+4  A: 
BEGIN {
    defaults["some.value"]  = "SOME VALUE"
    defaults["other.value"] = "OTHER VALUE"
}

{
    for (key in defaults) {
        pattern = key
        gsub(/\./, "\\.", pattern)
        if (match($0, "^" pattern "=.*")) {
            gsub(/=.*/, "=" defaults[key])
            delete defaults[key]
        }
    }
    print $0
}

END {
    for (key in defaults) {
        print key "=" defaults[key]
    }
}
Sean Bright
*The* solution. The order of the default keys is hash dependent, so the final not-used keys would not show up in the same order as the declaration, but that's a minor issue.
rq
@Sean Bright: Beautiful, thanks. Bonus question: how would I regex-escape the key in your for loop? It works now, but the dots are actually only matching by accident.
Tomalak
@Tomalak: See my edit
Sean Bright
Ah, I see. Fair enough. I hoped for a magic "quote regex" function, but this will do. :)
Tomalak
Ah. There may be one but I don't know awk, sadly.
Sean Bright
After I've read some more in the docs -- A more generic way to regex-escape a string in awk would be like this: pattern = gensub(/([.(){}+*?|\[\]\\])/, "\\\\\\1", "g", key)
Tomalak