GBC
GBC

Reputation: 3326

Matching and replacing multiple $variables in line

I'm attempting to write a very basic shell to python converter, and I'm having some trouble replacing variables. Example, I want:

echo $var1 $var2 $var3
echo $var1 $var2 $var3 $var4

to become:

print var1, var2, var3
print var1, var2, var3, var4

currently, it becomes:

print var1, var2 var3
print var1, var2 var3, var4

My regex is:

$string =~ s/(\$[a-z]+[a-z0-9]*)(\s+\S+)/$1,$2/gi;

for some reason, it doesn't apply the regex again to the second part of the matched string, even though the global modifier is set.

Very thankful for any help!

Upvotes: 1

Views: 838

Answers (4)

Greg Bacon
Greg Bacon

Reputation: 139531

Make your substitution conditional on whether the variable occurs at the end of the line with

$string =~ s{\$([a-z]+[a-z0-9]*)(\s*$)?}{ defined $2 ? $1 : "$1," }ge;

The subpattern that corresponds to $2 is (\s*$)?. The ? makes the match optional, so $2 will be defined only when that pattern matches successfully, that is, for the last variable on the line—even in the presence of invisible trailing whitespace. For an “interior” variable, append a comma to the end. Otherwise, the variable name only will do.

For example

#! /usr/bin/env perl

use strict;
use warnings;

while (defined(my $string = <DATA>)) {
  $string =~ s{\$([a-z]+[a-z0-9]*)(\s*$)?}{ defined $2 ? $1 : "$1," }ge;
  $string =~ s/\becho\b/print/g;  # for demo only
  print $string, "\n";
}

__DATA__
echo $var1 $var2 $var3
echo $var1 $var2 $var3 $var4

Output:

print var1, var2, var3
print var1, var2, var3, var4

Upvotes: 0

Hameed
Hameed

Reputation: 2277

Keeping close to your regex, try this:

use strict;
use warnings;

my $string = '$var1 $var2 $var3 $var4$var5';

$string =~ s/\$([a-z][a-z0-9]*)\s*/$1, /gi;
$string =~ s/,\s*$//; #removing trailing comma
print $string . "\n";

Output:

var1, var2, var3, $var4

Or I would simply do this, which may or may not work with some other things you may have.

s/\$(\w+)\s*/$1, /g

Upvotes: 1

justintime
justintime

Reputation: 3631

Once it has matched the the (\s+\S+) the regex engine has moved passed the 2nd variable. A lookahead assertion will allow it to check what is ahead without consuming the string

 $string =~ s{
   \$ 
   ( [a-z]+[a-z0-9]* )  # capture varname
   (?=\s+\S+)           # lookahead
 } {$1,}gix ;

Note that I am neither matching nor capturing the \s+S+ so no need to include it as $2 in the replacement string. And the /x allows me to space the regex out and add add comments.

Note - other answers may give a closer match to what you are tring to do. I am just explaining your specific query about not matching the 2nd variable.

Upvotes: 0

Kash
Kash

Reputation: 9029

You would just need to modify your regex to:

$string =~ s/\$([a-z][a-z0-9]*)(?:\s*|$)/$1, /gi; 
$string =~ s/,\s*$//; #courtesy Hameed

The non capturing group would ensure that the variable ends either with a space, or no space at all or would be at the end of the line.

This would also match echoes like $var1 $var2 $var3 $var4$var6 and output var1, var2, var3, var4, var5, var6

Upvotes: 0

Related Questions