bash/sed, replace line in file at same indentation level

Viewed 573

I'm looking to replace lines in a file. i.e. if "oldtext" is in the file at the start of a line, replace the whole line by "newtext".

Start of the line can be ^oldtext or oldtext will be indented by some whitespace/tabs

grep "^[\S]oldtext" myfile.txt   # doesn't work

I need to calculate the same indentation level that "oldtext" had

prefixspaces = <number of spaces before the "mytext", converting tabs to 4 spaces>

I realise that sed is maybe the best way to achieve this, but I somehow need to calculate the prefix space first, then my output is something like:

sed -i -e 's/^$prefixspaces$newtext*$/$prefixspaces$newtext/g' myfile.txt

Can't work out how to calculate this. Maybe the $prefixspace can be held in a group () or something so that sed can easily calculate and work with it? Possibly this is a sed or awk one-liner, but I can't work it out after some searching.

3 Answers

Like this:

sed 's/^\( *\)oldtext.*/\1newtext/' file

Explanation:

search

^             beginning of the line
\( *\)        capture 0 or more spaces in group 1
oldtext       'oldtext'
.*            rest of the line

replace

\1            the 0 or more spaces from group1
newtext       'newtext'

With sed

Another way to do, additionaly to the accepted answer, is using class characters ([[:space:]]), and extender regular expressions with the -r parameter:

$ sed -r 's/^([[:space:]]*)oldtext.*/\1newtext/' << _eof
  oldtext this
 a
    oldtext also this
_eof

And the result will be:

  newtext
 a
    newtext

In ([[:space:]]*), we captured a group with zero or more spaces, but we can specify a certain number.

With awk

We can use gsub to replace spaces and count the times:

$ awk \
    -v old="oldtext" \
    -v new="newtext" \
    'BEGIN{re="^[[:space:]]*"old}
{
  if (match($0,re)) {
    i=gsub(/ /,"")
    while (--i) printf " "
    print new
  }
  else 
    print
}' << _eof
  oldtext this
oldtext also this
    oldtext and this
  oldtext also this
_eof

And the result will be:

  newtext
 newtext
     newtext
   newtext

With perl

We can achieve this with the use of perl in a sed-like style:

$ perl -lpe 's/^(\s*)oldtext.*/\1newtext/' << _eof
  oldtext this
oldtext this
    oldtext this
  oldtext also this
_eof

And the result will be:

  newtext
newtext
    newtext
  newtext

This might work for you (GNU sed):

sed '/^\s*oldtext/s/\S.*/newtext/' file

Match oldtext at the start of a line or indented and replace from the first non-white space character.

Related