6

I have a file which has variables from G1 to G229. I want to replace them with G230 to G469; how I can do that? I tried this bash script but it didn't work:

#!/bin/bash

for num in {1..229}
  do
  echo G$num
  N=$(($num+229))
  echo G$N  
  sed -i -e 's/$G$num/$G$N/g' file
done
2
  • 2
    wouldn't 229 + 229 be 458, not 469? Simple keyboard-typo?
    – Jeff Schaller
    Aug 31, 2017 at 22:30
  • yes, my apologize, it is 458! Sep 2, 2017 at 20:48

3 Answers 3

8

That's more a job for perl (which GNU sed copied that -i from by the way):

perl -pi -e 's{\$G\K\d+}{
  if ($& >= 1 && $& <= 229) {
    $& + 229
  } else {
    $&
  }}ge' file

Note that it would also change G0001 to G230. If you don't want that, you can change the \d+ to [1-9]\d*.

With your approach, even if you fixed the quoting, that would rewrite the file 229 times and also not work as you'd wish.

A s/\$G1/\$G230/g, would change $G12 to $G2302, and the next s/\$G2/\$G231/g would then change that to G231302 (which the s/\$G23/\$G252 would change to G2521302 and so on).

3

Single quotes in bash stop variable expansion so the following is looking for the literal $G$num to replace with the literal $G$N

sed -i -e 's/$G$num/$G$N/g' file

You want bash expansion to happen so you must use double quotes and escape any special characters manually:

sed -i -e "s/\$G$num/\$G$N/g" file

You can see the difference with the following script:

#!/bin/bash

for num in {1..229}; do
  N=$(($num+229))
  echo '$G$num $G$N' vs "\$G$num \$G$N"
done

Produces

$G$num $G$N vs $G1 $G230
$G$num $G$N vs $G2 $G231
$G$num $G$N vs $G3 $G232
...
$G$num $G$N vs $G228 $G457
$G$num $G$N vs $G229 $G458
1
  • Thank you for your answer, but when I executed this code as Jeff said it gives very big numbers for example G2542130 for G1 Sep 2, 2017 at 18:11
2

sed solution. Maybe it too tricky and unoptimal, but it works. As an experiment :).

It do all replacements in the one sed call by executing the one, big command sequence, generated by printf and paste usage. I wanted split this command to the multiline for readability, but couldn't - it stops working then. So - the oneliner:

sed -i -r "$(paste -d'/' <(printf 's/%s\\b\n' G{1..229}) <(printf '%s/g\n' G{230..458}))" file.txt

It is converting to the following sed command:

sed -i -r "s/G1\b/G230/g
s/G2\b/G231/g
s/G3\b/G232/g
s/G4\b/G233/g    
...
s/G227\b/G456/g
s/G228\b/G457/g
s/G229\b/G458/g" file.txt

Explanation

  1. sed -i -r "$(
  2. paste -d'/' - joins left and right parts (which are generated in 3,4 steps) by the slash - / and the result is this: s/G1\b/G230/g
  3. <(printf 's/%s\\b\n' G{1..229}) - makes left parts of the sed substitute command. Example: s/G1\b, s/G2\b, s/G3\b, so on.
    • \b - Matches a word boundary; that is it matches if the character to the left is a “word” character and the character to the right is a “non-word” character, or vice-versa. Information - GNU sed, regular expression extensions.
  4. <(printf '%s/g\n' G{230..458}) - makes right parts of the sed substitute command. Example: G230/g, G231/g, G232/g, so on.
  5. )" file.txt - input file.

Testing

Input

var G1 = value;
G3 = G1 + G2; 
G3 = G1 + G2
G3 = ${G1} + G2
var G2 = value;
var G3 = value;
G224 = G3 + G215;
G124 = G124 + G215;
G124 = G124 + G12;
var G4 = value;
var G5 = value;
var G6 = value;
var G59 = value;
var G60 = value;
var G156 = value;
var G227 = value;
var G228 = value;
var G229 = value;

Output

var G230 = value;
G232 = G230 + G231;
G232 = G230 + G231
G232 = ${G230} + G231
var G231 = value;
var G232 = value;
G453 = G232 + G444;
G353 = G353 + G444;
G353 = G353 + G241;
var G233 = value;
var G234 = value;
var G235 = value;
var G288 = value;
var G289 = value;
var G385 = value;
var G456 = value;
var G457 = value;
var G458 = value;
1
  • Thank you for your complet explanation! just the one line worked very well. Sep 2, 2017 at 20:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .