paxdiablo
paxdiablo

Reputation: 881103

Why is Perl complaining about an unterminated string here?

I have a Perl script which runs fine under Perl 5.005 on HPUX 11i v3 and that causes a small problem under Perl 5.10 on my Ubuntu 11.04 box.

It boils down to a line like:

open (CMD, "do something | grep -E 'sometext$' |");

(it's actually capturing ps -ef output to see if a process is running but I don't think that's important (a)).

Now this runs fine on the HPUX environment but, when I try it out under Ubuntu, I get:

sh: Syntax error: Unterminated quoted string

By inserting copious debug statements, I tracked it down to that offending line then started removing characters one-by-one until it stopped complaining. Luckily, $ was the first one I tried and it stopped giving me the error, so I then changed the line to:

open (CMD, "do something | grep -E 'sometext\$' |");

and it worked fine (under Linux anyway - I haven't tested that on HPUX since I don't have access to that machine today - if it does work, I'll just use that approach but I'd still like to know why it's a problem).

So it seems obvious that the $ is "swallowing" the closing single quote under my Linux environment but not apparently on the HPUX one.

My question is simply, why? Surely there weren't any massive changes between 5.005 and 5.10. Or is there some sort of configuration item I'm missing?


(a) But, if you know a better way to do this without external CPAN modules (ie, with just the baseline Perl 5.005 installation), I'd be happy to know about it.

Upvotes: 2

Views: 2633

Answers (6)

ikegami
ikegami

Reputation: 385546

Use the following!!!

use strict;
use warnings;

You would have gotten

Use of uninitialized value $' in concatenation (.) or string

Upvotes: 6

Eric Strom
Eric Strom

Reputation: 40142

A sigil followed by any punctuation symbol (on a standard keyboard) is a variable in Perl, regardless of if it is defined or not. So in a double quoted string [$@][symbol] will always be read as one token and interpolated unless the sigil is escaped.

I have a feeling that the difference you are seeing has to do with different system shells rather than different versions of perl.

Consider your line:

open (CMD, "do something | grep -E 'sometext$' |");

When perl sees that, it will interpolate the empty $' variable into the double quoted string, so the string becomes:

open (CMD, "do something | grep -E 'sometext |");

At that point, your shell gets to process a line that looks like:

do something | grep -E 'sometext

And if it succeeds or fails will depend on the shell's rules regarding unterminated strings (some shells will complain loudly, others will automatically terminate the string at eof).

If you were using the warnings pragma in your script, you probably would have gotten a warning about interpolating an undefined variable.


A shorter and cleaner way to read in the output of ps would be:

my @lines = grep /sometext\$/, `ps -ef`;

Or using an explicit open:

my @lines = grep /sometext\$/, do {
   open my $fh, '|-', 'ps -ef' or die $!;
   <$fh>
};

Upvotes: 4

netvope
netvope

Reputation: 7947

Because $' is a special variable in recent versions of Perl.

From the official documentation (perlvar):

$': The string following whatever was matched by the last successful pattern match (not counting any matches hidden within a BLOCK or eval() enclosed by the current BLOCK).

If there were no successful pattern matches, $' is empty and your statement essentially interpolates to

open (CMD, "do something | grep -E 'sometext |");

Escaping the dollar sign (the solution that works for you on Linux) should work on HPUX too.

I'm not sure when was this variable added, but I can confirm that it exists in Perl 5.8.5. What's New for Perl 5.005 mentions $' (not as a new feature), so I think it was already there before then.

Upvotes: 3

Jonathan Leffler
Jonathan Leffler

Reputation: 753475

You should probably use single-quotes rather than double quotes around the string since there is nothing in the string that should be interpolated:

open (CMD, q{do something | grep -E 'sometext$' |});

Better would be to use the 3-argument form of open with a lexical file handle:

open my $cmd, '-|', q{do something | grep -E 'sometext$'} or die 'a horrible death';

I don't have a good explanation for why $' is being recognized as the special variable in 5.10 and yet it was not in 5.005. That is unexpected.

Is there a really good reason you can't upgrade to something like 5.14.1? Even if you don't change the system-provided Perl, there's no obvious reason you can't install a recent version in some other location and use that for all your scripting work.

Upvotes: 2

evil otto
evil otto

Reputation: 10582

$' is a special variable (see perldoc perlvar). 5.005 was many versions ago, so it's possible that something has changed in the regexp engine to make this variable different (although it appears to be in 5.005 also)

As for the better way, you could at least only run the 'ps -ef' in a pipeline and do the 'grep' in perl.

Upvotes: 7

TLP
TLP

Reputation: 67900

$' is a special variable.

If you want to avoid variable expansion, just use q():

open (CMD, q(do something | grep -E 'sometext$' |));

Upvotes: 1

Related Questions