0

Can sed handle a substitution and simultaneously evaluate an expression to substitute somehow?

For example, I have the following entry in a text file:

##################################### topd Tree1 - Tree14 #######################################

For reasons I won't go in to, each Tree's number is N+1 relative to another numbering scheme I've been using so far.

Could sed (or any util really, sed is just my go to for find-and-replace operations), find every instance of TreeN and replace them with TreeN-1 such that the above line would then look like:

##################################### topd Tree0 - Tree13 #######################################

(Using Ubuntu 12.02, so pretty much anything goes.)

Joe Healey
  • 1,232
  • 3
  • 15
  • 34

4 Answers4

4

Using perl

perl -pe 's/Tree\K\d+/$&-1/ge' file

Output

##################################### topd Tree0 - Tree13 #######################################
123
  • 10,778
  • 2
  • 22
  • 45
  • using perl seems to be the easiest solution. – sjsam Jul 13 '16 at 10:33
  • Nice, I ended up trying this one first so accepted as the answer. To make it a bit more informative, could you elaborate on which bits of syntax are doing what? I can pick apart elements but not all of it. – Joe Healey Jul 13 '16 at 10:59
  • @JoeHealey `\K` cause the replacement to only affect the right hand side, so only the digits `\d+` are matched. `$&` is matched string. `g` is global, as in make as many matches as many as possible. `e` evaluate the replacement part of the regex as though it were a command. – 123 Jul 13 '16 at 12:22
1

I'd use perl in this case, with the e modifier to evaluate an expression in the replacement part:

perl -pe 's/(Tree)(\d+)/$1 . ($2 - 1)/ge'

You can also take advantage of more advanced regular expression support, e.g. by using a lookbehind assertion:

perl -pe 's/(?<=Tree)(\d+)/$1 - 1/ge'
Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
0

You can try this;

sed 's/[^0-9,-]*//g' filename | sed -e "s/-/ /g" | awk '{print "##################################### topd Tree"$1-1 " - Tree" $2-1 " #######################################"}'

awk '{gsub(/Tree/,"Tree ",$0);print $1" "$2" "$3$4-1" "$5" "$6$7-1" "$8}' filename
Mustafa DOGRU
  • 3,994
  • 1
  • 16
  • 24
  • 1
    A single awk solution is possible for this. This looks two `sed`s and `awk` seems redundant – sjsam Jul 13 '16 at 10:35
0

Using gnu awk below

awk '{
    for(i=1;i<=NF;i++){
     $i ~ /^Tree[[:digit:]]+$/
     {
     n=(gensub(/Tree/,"",1,$i)-1);
     $i=gensub(/Tree([[:digit:]]+)/,"Tree"n,1,$i);
     }
     }
     print}' file

would do the trick

sjsam
  • 21,411
  • 5
  • 55
  • 102