0

We want to find a specific line in a file and change a value 3 lines after the match.

For example we are searching for the string "class_rproxy"

cat /var/git/control/Puppetfile | grep -A 3 class_rproxy

Result:

# class_rproxy
mod 'rproxy',
        :git => 'git@abcde.de:PE/rproxy.git',
        :tag => '1.3.59'

We want to change the value "1.3.59" to "12.4.60"

How can we do that to change only this value without manualy editing it.

Thanks

  • Have you tried anything? – oguz ismail Jun 05 '19 at 12:03
  • @oguzismail I do not have a clue how to do that. In the same file there is a bunch of this lines. – André Deseive Jun 05 '19 at 12:12
  • `sed '/class_rproxy/{N;N;N;s/1\.3\.59/12.4.60/}' file` may work for this specific case, also take a look at [tag:awk]. But anyways, if you didn't try anything on your own most people will ignore this question even if they have knowledge to help. – oguz ismail Jun 05 '19 at 12:21
  • 1
    @oguzismail Thank you very much for this. I tried many things without success. – André Deseive Jun 05 '19 at 12:47
  • 1
    Welcome. Doesn't matter if they failed, you should include them in your question so people will see at least you've tried to solve your problem on your own and you will be more likely to get a decent answer. – oguz ismail Jun 05 '19 at 12:54
  • 1
    Do you **really** want to change the specific string `1.3.59` or do you actually just want to change whatever value is associated with `:tag =>`? If `21.3.597` existed at that line would you want it changed to `212.4.607` or not? – Ed Morton Jun 05 '19 at 13:51

2 Answers2

1

Here's one possible interpretation of your requirements:

$ awk '/class_rproxy/{c=4} c&&!(--c){sub(/[0-9.]+/,"12.4.60")} 1' file
foo
# class_rproxy
mod 'rproxy',
        :git => 'git@abcde.de:PE/rproxy.git',
        :tag => '12.4.60'
bar

See https://stackoverflow.com/a/17914105/1745001 for how that works. If that's not exactly the replacement you want then just change the sub(/[0-9.]+/,"12.4.60") to be whatever it is you do want.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
1

Here's my take on it.

awk -v ver="12.4.60" '/^#/{n=0} /^# class_rproxy$/{n=1} n && $1==":tag" {sub(/[0-9]+\.[0-9.]+/, ver)} 1' puppetfile

Here are the parts:

  • -v ver="12.4.60" - set your version as a variable awk can use
  • /^#/{n=0} - on any comment, set a semaphore to "false"
  • /^# class_rproxy$/{n=1} - and when we see the correct section, set semaphore to "true"
  • n && $1==":tag" - if the semaphore is set and we're at the right line...
  • {sub(/[0-9]+\.[0-9.]+/, ver)} replace whatever numbers we find with the variable,
  • 1 - and print.

Rather than counting down the lines, as Ed has done, this creates a small state machine that uses the semaphore to trigger a change to the value of :tag based on the preceding comment. If you wanted to make the tool a little more generic, you could even make section detection based on a variable on the command line (-v). For example:

awk -v ver="12.4.60" -v sect="class_rproxy" '
  BEGIN {
    s="^# " sect "$"
  }
  /^#/ {n=0}
  $0 ~ s {n=1}
...
ghoti
  • 45,319
  • 8
  • 65
  • 104