Zaid
Zaid

Reputation: 37146

Perl regular expressions in system calls

The following Perl one-liner works as I expect it to; it strips my file of leading and trailing backspaces and replaces intermediate whitespaces with a single tab:

$ perl -pi -le 's/^\s+//; s/\s+$//; s#\s+#\t#g;' file

What perplexes me is why I cannot get this to work via a system call from within my Perl code:

system "perl -pi -le 's/^\s+//; s/\s+\$//; s#\s+#\t#g;' file";  # '$' backslashed

What's the issue here?

Upvotes: 1

Views: 1785

Answers (8)

runrig
runrig

Reputation: 6524

There's no need to launch another process for -i, but in general you should localize some of the global variables:

sub do_stuff {
  my $file = shift;
  local ($_, $., $ARGV, *ARGV);
  local ( $^I, @ARGV ) = ( '.bak', $file );

  while ( <> ) {
    s/..../..../;
    print;
  }
}

Upvotes: 3

tadmc
tadmc

Reputation: 3744

This is just the situation that Larry gave us alternate quote characters for. We would like to use single quotes for system()s arg so that we don't have to figure out what needs backslashing, but we also need single quotes in the quoted string itself. So, alternate quote characters to the rescue!

system q(perl -p -le 's/^\s+//; s/\s+$//; s#\s+#\t#g;' file);

Now you can copy/paste what works at the command line directly into your Perl source.

Upvotes: 0

Yi Zhao
Yi Zhao

Reputation: 6784

for Linux try

system q(perl -pi -le 's/^\s+//; s/\s+$//; s#\s+#\t#g;' file); # comments here

for M$ windows:

system q(perl -pi -le "s/^\s+//; s/\s+$//; s#\s+#\t#g;" file); # comments here

Upvotes: 0

Schwern
Schwern

Reputation: 164769

runrig showed how you don't have to make a subprocess just to use -i. Using -i internally is a bit weird, so there's two cleaner alternatives. The first is to use Tie::File which is fairly straightforward.

The other is to write to a temp file using File::Temp which is essentially what -i does.

my $tmp = File::Temp->new;
open my $fh, "<", $file or die "Can't open $file: $!";

while(<$fh>) {
    s/.../.../;
    print $tmp $_;
}

my $tmpfile = $tmp->filename;
rename $tmpfile, $file or die "Can't rename $tmpfile to $file: $!";

Upvotes: 1

Axeman
Axeman

Reputation: 29854

Inside double quotes ' has no functional value. Your string will be edited by Perl. My tests show that you are losing the \s expressions. It's also interpolating the '\t'. tab--although that might not be so much the problem.

Here's Data::Dumper's take:

$s = 'perl -pi -le \'s/^s+//; s/s+$//; s#s+#    #g;\' file';

Upvotes: 0

ikegami
ikegami

Reputation: 385657

say "perl -pi -le 's/^\s+//; s/\s+\$//; s#\s+#\t#g;' file";

produces

Unrecognized escape \s passed through at -e line 1.
Unrecognized escape \s passed through at -e line 1.
Unrecognized escape \s passed through at -e line 1.
perl -pi -le 's/^s+//; s/s+$//; s#s+#   #g;' file

You want

system("perl -pi -le 's/^\\s+//; s/\\s+\$//; s#\\s+#\\t#g;' file");

Actually, why invoke the shell at all?

system('perl', '-i', '-ple' 's/^\\s+//; s/\\s+$//; s#\\s+#\\t#g;', 'file');

Upvotes: 2

Brian Roach
Brian Roach

Reputation: 76898

The double-quotes are interpreting everything.

try:

system "perl -pi -le 's/^\\s+//; s/\\s+\$//; s\#\\s+\#\\t\#g;' file";

Upvotes: 0

cjm
cjm

Reputation: 62099

You're forgetting that the backslashes get parsed first as a Perl double-quoted string, and then as a shell command.

The equivalent of the command line:

perl -pi -le 's/^\s+//; s/\s+$//; s#\s+#\t#g;' file

in a Perl script is

system "perl -pi -le 's/^\\s+//; s/\\s+\$//; s#\\s+#\\t#g;' file"

Although it's silly to launch another copy of Perl just for this. You're probably better off using a few more lines of code and doing it in the same process.

Upvotes: 1

Related Questions