Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The difference between " and ' in Perl

According to this comment:

You also should not use single quotes in print ">>${ '_<$filename' }<<\n". Instead try: print ">>${ \"_<$filename\" }<<\n"

I always thought that differences between " and ' are only that the string is interpreted or not.

I want to ask why at this context:

print ">>${ \"_<$filename\" }<<\n"
print ">>${  '_<$filename'  }<<\n"

perl prints different values?

Why I should use \" instead of just ' here?

like image 517
Eugen Konkov Avatar asked Dec 17 '22 17:12

Eugen Konkov


1 Answers

The short answer to your question is that $filename is interpolated in your first example, whereas it's not in the second example. Why, you ask? Oh, boy. Strap in for a bumpy ride.

First, understand what the ${...} notation is for. There are two reasons to use it: one is to simply distinguish a variable name from surrounding text; the other is to dereference the value returned by a BLOCK (of code).

If the contents of the brackets are just a bareword (with possible surrounding whitespace), then it's just a simple variable name. Thus the string "$foo" and the string "${foo}" and the string "${ foo }" all produce the same result. This is handy if you need to append text directly after a variable in a string: instead of saying $foo.'bar', you can say "${foo}bar". The notations work outside interpolating strings too: you can say ${foo} = 'bar' if you like.

But that's not what your code is doing. Oh, no. You're aiming the double-barrel shotgun of symbolic dereferencing directly at your feet. You're evaluating the contents of the brackets as code, and using the result as a reference to a variable. You're making it doubly confusing by putting the whole lot in an interpolating string. Let's not go any deeper than necessary with the concept of dereferencing, since that's not the main question here. The key point is that the stuff in curly brackets is effectively being eval'd if it's not a bareword.

Now, understand that when Perl reaches the "${" notation inside your interpolating string, it has to make a decision about what it's interpolating here: an ordinary variable name or code. Without interpolating anything else, it seeks the matching close-bracket. If the sub-string delimited by those brackets doesn't look like a bareword, then it's going to be eval'd. You still need to escape double-quotes in this region, because any unescaped double quote would count as the final delimiter for the outer string of which this is a subset, and the remainder of your "block" would be outside the string.

With that in mind, we can see that your first example evals "_<$filename", whereas your second example evals '_<$filename'. That's just the difference between an interpolating string and not: the first has $filename replaced by something, whereas the second one is strictly literal.

In both cases, the string is used to perform symbolic dereferencing on a global scalar variable with a ghastly name, and shame on you if you actually defined it. The less said about it, the better.

If you think I'm kidding about the contents of the curly brackets being effectively eval'd try this code.

print "This, ${die qq(oops!\n)}, and this.\n";

Of course, the key difference between in-string code like this and eval() proper is that eval() would catch the exception and set $@. This doesn't: it just dies as it would in an ordinary code block.

This gives rise to one of Perl's hidden tricks. You can't interpolate a method call like $foo->bar, but you can dereference a reference to an array that contains it, like so.

print "@{[$foo->bar]}\n";

In summary then, for the love of God please don't write code like that.

like image 75
TFBW Avatar answered Dec 30 '22 12:12

TFBW