0

I have the following code:

function replaceappend() {
    awk -v old="^$2" -v new="$3" '
        sub(old,new) { replaced=1 }
        { print }
        END { if (!replaced) print new }
    ' "$1" > /tmp/tmp$$ &&
    mv /tmp/tmp$$ "$1"
}

replaceappend "/etc/ssh/sshd_config" "Port" "Port 222"

It works perfectly but I am looking to modify it so it replaces the entire lines contents rather than just the matching text.

At the moment it would do this:

Port 1234 -> Port 222 1234

I want it to be work like this:

Port 1234 -> Port 222

I closest code I can find to do this is found here:

awk 'NR==4 {$0="different"} { print }' input_file.txt

This would replace the entire line of the match with the new content. How can I implement this into my existing code?

Community
  • 1
  • 1
Jimmy
  • 12,087
  • 28
  • 102
  • 192

3 Answers3

3

Just change:

sub(old,new) { replaced=1 }

to:

$0~old { $0=new; replaced=1 }

or:

sub(".*"old".*",new) { replaced=1 }
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
2

If you want to replace the entire line you can simplify your function. To avoid problems with metacharacters in the variables you pass to awk, I would suggest using a simple string search too:

awk -vold="$2" -vnew="$3" 'index($0,old)==1{f=1;$0=new}1;END{if(!f)print new}' "$1"

index returns the character position of the string that you are searching for, starting at 1. If the string old is at the start of the line, then the line is changed to the value of new. The 1 after the block is always true so every line is printed (this is a common shorthand for an unconditional {print} block).

As mklement0 has pointed out in the comments, the variables you pass to awk are still subject to some interpretation: for example, the string \n will be interpreted as a newline character, \t as a tab character, etc. However, this issue is much less significant than it would be using regular expressions, where things like a . would match any character.

Community
  • 1
  • 1
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • 1
    Great simplification; a hypothetical caveat (not a problem with the OP's example): passing a value via `-v ...` is still subject to _some_ interpretation by `awk`: try `awk -v v='a\nb' 'BEGIN { print v }'`. – mklement0 Mar 15 '15 at 22:22
1

Again, use a regular expression for that which you want to replace:

replaceappend port.txt "Port.*" "Port 222"

Here you are replacing Port (if it starts the line, as per your function definition) plus whatever follows until the end of the line with "Port 222".

EDIT: To make this part of the function instead of requiring it in the call, modify it to

function replaceappend() {
    awk -v old="^$2.*" -v new="$3" '                                                                                                        
        sub(old,new) { replaced=1 }                                                                                                         
        { print }                                                                                                                           
        END { if (!replaced) print new }                                                                                                    
    ' "$1" > /tmp/tmp$$ &&
    mv /tmp/tmp$$ "$1"
}
jas
  • 10,715
  • 2
  • 30
  • 41
  • Could you tell me how to integrate this into the function instead. Would it be this? ```awk -v old="^$2.*" -v new="$3" '```? I've added the period and the star as per your answer. – Jimmy Mar 15 '15 at 21:38
  • I always want the entire line to be replaced so I think your example with the asterisk is correct, but just that I want it on the function itself and not the command to call the function – Jimmy Mar 15 '15 at 21:41
  • 1
    Yes, that would work fine. I'll put that in the answer as well. – jas Mar 15 '15 at 21:41