variables inside a string

M

Martin Keiter

Hi,

I would like to do something like this:

$sentence = "$1 is here!";
....
if ($string =~ m/(.*) comes in/) {
print $sentence;
}

This of course does not work, as $1 is evaluated when $sentence is
initialized. But also setting

$sentence = "\$1 is here!"

does not work - it just prints

$1 is here!

is there a way to evaluate th "$1" inside of $sentence at the time when
$sentence is used?
 
J

Jürgen Exner

Martin Keiter said:
is there a way to evaluate th "$1" inside of $sentence at the time when
$sentence is used?

If you want to evaluate a string I suggest you look at the eval()
function.

jue
 
M

Martin Keiter

Ok, I found "eval", and it works, although I do not yet fully understand
why :)

This does what I want:

my $sentence = "\$1 is here!";
my $string = "foo comes in";

if ($string =~ m/(.*) comes in/) {
my $code =" \"$sentence\" ";
print "code: $code\n";
my $bar = eval $code;
print "$bar\n";
}

but do I really have to introduce this additional variable $code ?
I tried:
eval \"$sentence\";
eval "$sentence";
eval $sentence;
but all of them produce errors...
 
W

Wolf Behrenhoff

Ok, I found "eval", and it works, although I do not yet fully understand
why :)

This does what I want:

my $sentence = "\$1 is here!"; ^^^^
my $string = "foo comes in";

if ($string =~ m/(.*) comes in/) {
my $code =" \"$sentence\" "; ^^^^^^^^^^^^^^^^^^
print "code: $code\n";
my $bar = eval $code;
print "$bar\n";
}

That's awful to read. Don't use too many \ if they can be avoided
easily. Use q (or ') or qq instead.
but do I really have to introduce this additional variable $code ?
I tried:

Try
print eval qq("$sentence");

Another way without eval is:

sub sentence { "$1 is here" }
.... print sentence;


Wolf
 
K

Krishna Chaitanya

[text removed]

It may help you to check "How can I expand variables in text strings?"
in Perl FAQ 4. Your example's harmless for educational purposes, but,
for any serious code, be careful for any unpredictability or tainted
text in $string (especially if it comes from an outside source). Also,
if you use eval, check for $@. Of course, nothing beats avoiding this
problem in the 1st place...don't have double interpolations unless
you've a very good reason to.
 
M

Martijn Lievaart

Hi,

I would like to do something like this:

$sentence = "$1 is here!";
...
if ($string =~ m/(.*) comes in/) {
print $sentence;
}

This of course does not work, as $1 is evaluated when $sentence is
initialized. But also setting

$sentence = "\$1 is here!"

does not work - it just prints

$1 is here!

is there a way to evaluate th "$1" inside of $sentence at the time when
$sentence is used?

$sentence =~ s/\$1/$1/;

HTH,
M4
 
M

Martin Keiter

It may help you to check "How can I expand variables in text strings?"
in Perl FAQ 4. Your example's harmless for educational purposes, but,
for any serious code, be careful for any unpredictability or tainted
text in $string (especially if it comes from an outside source). Also,
if you use eval, check for $@. Of course, nothing beats avoiding this
problem in the 1st place...don't have double interpolations unless
you've a very good reason to.

As I only today discovered "eval" I was not aware of potential dangers.
Thank you very much for pointing that out!
(yes, the string will come from an outside source!)
 
M

Martin Keiter

$sentence =~ s/\$1/$1/;

this produces:
Use of uninitialized value in substitution iterator at .//test.pl line 13.

what I do now is:

my $sentence = "\$1 is here!";
my $string = "foo comes in";
my $reg = "(.*) comes in";

my $text = $sentence;
if ($string =~ m/$reg/)
{
foreach my $i (1..9) {
if (${$i}) {
my $tmp = ${$i};
$text =~ s/\$$i/$tmp/
}
}
print "$text\n";
}


no eval any more, so it should be safe, although I find it a bit ugly.
 
J

J. Gleixner

Martin said:
this produces:
Use of uninitialized value in substitution iterator at .//test.pl line 13.

what I do now is:

my $sentence = "\$1 is here!";
my $string = "foo comes in";
my $reg = "(.*) comes in";

my $text = $sentence;
if ($string =~ m/$reg/)
{
foreach my $i (1..9) {
if (${$i}) {
my $tmp = ${$i};
$text =~ s/\$$i/$tmp/
}
}
print "$text\n";
}


no eval any more, so it should be safe, although I find it a bit ugly.

Maybe you're making this more complex than you need to. Provided your
example is close to your actual code you could simplify it a lot.

if( $string =~ m/$reg/ ){ print "$1 is here"; }
 
J

Jürgen Exner

Martin Keiter said:
Ok, I found "eval", and it works, although I do not yet fully understand
why :)

Actually it is very simple.
You just have to understand the difference between code and data.
This does what I want:

my $sentence = "\$1 is here!";

This line, including the double-quoted text is code. It is executed at
runtime and at that moment the content of the variable becomes data.
Obviously you cannot execute data.
my $bar = eval $code;

Exception being eval(), which converts data (i.e. text) back into code,
such that it can be executed again.
my $code =" \"$sentence\" ";
but do I really have to introduce this additional variable $code ?
I tried:
eval \"$sentence\";
eval "$sentence";
eval $sentence;
but all of them produce errors...

The thingy that you pass to eval must be legal Perl code.
$1 is here!
is not legal Perl code.

jue
 
M

Martin Keiter

Maybe you're making this more complex than you need to. Provided your
example is close to your actual code you could simplify it a lot.

if( $string =~ m/$reg/ ){ print "$1 is here"; }

the example is relatively close to my code, but of course I have
simplified a bit: actually I have regexps and corresponding sentences in
an array, and then do a test of $string against all regexps inside a
loop. Most of the sentences will not contain a "$1", but some will.


$test[0]{'regexp'} = "(.*) comes in";
$test[0]{'sentence'} = "\$1 is here!";
....
....

# actually this will come from the outside world:
my $string = "foo comes in";

foreach my $i (0..$#talk)
{
if ($string =~ m/$test[$1]{'regexp'} /)
{
$text = $test[$i]{'sentence'};
# now if there is a (XXX) inside of $test[$1]{'regexp'}
# replace $1 in $text by XXX
}
}
 
M

Martin Keiter

Actually it is very simple.

So I hoped!
You just have to understand the difference between code and data.

I think I did, but am quite shure, I do not yet understand all this
quoting stuff.
This line, including the double-quoted text is code. It is executed at
runtime and at that moment the content of the variable becomes data.
Obviously you cannot execute data.

agreed :)
Exception being eval(), which converts data (i.e. text) back into code,
such that it can be executed again.
ok.


The thingy that you pass to eval must be legal Perl code.
$1 is here!
is not legal Perl code.

ok.
But why does this work then?

my $sentence = "\$1 is here!";
my $code =" \"$sentence\" ";
eval $code;

is it that
"$1 is here!"
is passed to eval (including the quotes) and this is a valid expression?

Thanks for your patience!
 
S

sln

Maybe you're making this more complex than you need to. Provided your
example is close to your actual code you could simplify it a lot.

if( $string =~ m/$reg/ ){ print "$1 is here"; }

the example is relatively close to my code, but of course I have
simplified a bit: actually I have regexps and corresponding sentences in
an array, and then do a test of $string against all regexps inside a
loop. Most of the sentences will not contain a "$1", but some will.


$test[0]{'regexp'} = "(.*) comes in";
$test[0]{'sentence'} = "\$1 is here!";
...
...

# actually this will come from the outside world:
my $string = "foo comes in";

foreach my $i (0..$#talk)
{
if ($string =~ m/$test[$1]{'regexp'} /)
{
$text = $test[$i]{'sentence'};
# now if there is a (XXX) inside of $test[$1]{'regexp'}
# replace $1 in $text by XXX
}
}

If you going to use eval, you could always use the suggs posted
by others with something like this.

-sln

use strict;
use warnings;

my @template = (
{ regexp => q{(.*) comes in},
sentence => qq{"\$1 is here!\\n"}
},
{ regexp => q{(.*) sometimes comes in},
sentence => qq{"\$1 is sometimes there!\\n"}
},
);

my @instrings = (
'foo comes in here',
'bar sometimes comes in',
'baz comes in here',
);

for my $sample (@instrings)
{
for my $i (0..$#template)
{
if ( $sample =~ /$template[ $i ]{'regexp'}/ ) {
print eval $template[ $i ]{'sentence'};
}
}
}

__END__
 
W

Willem

Martin Keiter wrote:
)
)> $sentence =~ s/\$1/$1/;
)
) this produces:
) Use of uninitialized value in substitution iterator at .//test.pl line 13.

Obviously, since the substitution itself makes a new $1.

) what I do now is:
)
) my $sentence = "\$1 is here!";
) my $string = "foo comes in";
) my $reg = "(.*) comes in";
)
) my $text = $sentence;
) if ($string =~ m/$reg/)
) {
) foreach my $i (1..9) {
) if (${$i}) {
) my $tmp = ${$i};
) $text =~ s/\$$i/$tmp/
) }
) }
) print "$text\n";
) }
)
)
) no eval any more, so it should be safe, although I find it a bit ugly.

Try this:

if (my @matches = $string =~ m/$reg/) {
$text =~ s/\$(\d+)/$matches[$1]/g;
}


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
W

Willem

Willem wrote:
) Try this:
)
) if (my @matches = $string =~ m/$reg/) {
) $text =~ s/\$(\d+)/$matches[$1]/g;
) }

Oops, off-by-one error.

if (my @matches = $string =~ m/$reg/) {
$text =~ s/\$(\d+)/$matches[$1-1]/g;
}



SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
M

Martin Keiter

Willem wrote:
) Try this:
)
) if (my @matches = $string =~ m/$reg/) {
) $text =~ s/\$(\d+)/$matches[$1]/g;
) }

Oops, off-by-one error.

if (my @matches = $string =~ m/$reg/) {
$text =~ s/\$(\d+)/$matches[$1-1]/g;
}

That's good:
- it works
- I understand how it works
- it does not use eval :)

thank you!
 
M

Martin Keiter

If you going to use eval, you could always use the suggs posted
by others with something like this.

-sln

use strict;
use warnings;

my @template = (
{ regexp => q{(.*) comes in},
sentence => qq{"\$1 is here!\\n"}
},
{ regexp => q{(.*) sometimes comes in},
sentence => qq{"\$1 is sometimes there!\\n"}
},
);

I definitely have to learn about all this quoting! (you don't need to
explain it - I'll read and learn!
my @instrings = (
'foo comes in here',
'bar sometimes comes in',
'baz comes in here',
);

for my $sample (@instrings)
{
for my $i (0..$#template)
{
if ( $sample =~ /$template[ $i ]{'regexp'}/ ) {
print eval $template[ $i ]{'sentence'};
}
}
}

this also works. But is it not dangerous if the instrings are provided
by potentially dangerous users?
 
S

sln

If you going to use eval, you could always use the suggs posted
by others with something like this.

-sln

use strict;
use warnings;

my @template = (
{ regexp => q{(.*) comes in},
sentence => qq{"\$1 is here!\\n"}
},
{ regexp => q{(.*) sometimes comes in},
sentence => qq{"\$1 is sometimes there!\\n"}
},
);

I definitely have to learn about all this quoting! (you don't need to
explain it - I'll read and learn!
my @instrings = (
'foo comes in here',
'bar sometimes comes in',
'baz comes in here',
);

for my $sample (@instrings)
{
for my $i (0..$#template)
{
if ( $sample =~ /$template[ $i ]{'regexp'}/ ) {
print eval $template[ $i ]{'sentence'};
}
}
}

this also works. But is it not dangerous if the instrings are provided
by potentially dangerous users?

Eval in itself is not dangerous, its what's in the eval after its code.
If, after its code, its result is a STRING scalar as opposed to bareword functions,
thats all it is. It depends on how you quote it.

Consider this:

print eval qq{"system(\\"dir aa.*\\");\\n"};
print eval qq{system("dir aa.*");};

I think the first cannot be executed, so if you set up
'sentence' by qq{ " <capture from user> "} you are quoting "" and it
( here ^ and here ^ )
is a scalar.
Anyway, thats the theory I think, but there could be oddities I guess.

-sln
 
S

sln

If you going to use eval, you could always use the suggs posted
by others with something like this.
[...]
this also works. But is it not dangerous if the instrings are provided
by potentially dangerous users?

Eval in itself is not dangerous, its what's in the eval after its code.
If, after its code, its result is a STRING scalar as opposed to bareword functions,
thats all it is. It depends on how you quote it.

Consider this:

print eval qq{"system(\\"dir aa.*\\");\\n"};
print eval qq{system("dir aa.*");};

I think the first cannot be executed, so if you set up
'sentence' by qq{ " <capture from user> "} you are quoting "" and it
( here ^ and here ^ )
is a scalar.
Anyway, thats the theory I think, but there could be oddities I guess.

I'm going to have to backtrack on this whole thing.
So forget everything I said with the eval "" format.
It is not safe at all if using external, possibly malicious data.

The docs say that eval BLOCK is compiled only once. But that won't
do any good for dynamic variable substitution ala \$var.
The eval "" form will recompile every time.

The fact is that if a user knows (or guesses) your statement form,
he/she will be able to splice in code, in any construction of eval "".

I see no way around it, it is dangerous, so use another method.
Test case:

my $val = ").system('dir a*.*').qq(";
eval "print qq($val)";

-sln
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top