Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

help declaring variables within perl one-liner

by Special_K (Monk)
on Apr 01, 2021 at 16:34 UTC ( #11130676=perlquestion: print w/replies, xml ) Need Help??

Special_K has asked for the wisdom of the Perl Monks concerning the following question:

I have a file one_liner_wrapper.pl that contains the following:

#!/usr/bin/perl -w use strict; my $num_subs = `perl -w -0777 -pi -e 'my $c=s/foo/bar/g;print STDOUT " +$c\n"' ./foo_test`; printf("num_subs = $num_subs\n");

The file foo_test contains the following:

foo food fool foot

When I try to execute one_line_wrapper.pl, I receive the following error:

> ./one_liner_wrapper.pl Global symbol "$c" requires explicit package name (did you forget to d +eclare "my $c"?) at ./one_liner_wrapper.pl line 4. Global symbol "$c" requires explicit package name (did you forget to d +eclare "my $c"?) at ./one_liner_wrapper.pl line 4. Execution of ./one_liner_wrapper.pl aborted due to compilation errors.

This error also occurs if I omit the "my " in the above code.

Why I am I receiving this error despite the "my $c" that is included at the start of the statement? If I instead change one_liner_wrapper.pl to the following:

#!/usr/bin/perl -w use strict; my $c; my $num_subs = `perl -w -0777 -pi -e '$c=s/foo/bar/g;print STDOUT "$c\ +n"' ./foo_test`; printf("num_subs = $num_subs\n");

The output is:

> ./one_liner_wrapper.pl Use of uninitialized value $c in concatenation (.) or string at ./one_ +liner_wrapper.pl line 5. Use of uninitialized value $c in concatenation (.) or string at ./one_ +liner_wrapper.pl line 5. num_subs =

What is the proper way to declare variables that will be used only within perl one-liners called from another perl script when "use strict" is in effect?

Replies are listed 'Best First'.
Re: help declaring variables within perl one-liner
by hippo (Chancellor) on Apr 01, 2021 at 16:39 UTC

    Backticks interpolate, so you need to escape the dollars and backslashes:

    #!/usr/bin/perl -w use strict; my $num_subs = `perl -w -0777 -pi -e 'my \$c=s/foo/bar/g;print STDOUT +"\$c\\n"' ./foo_test`; printf("num_subs = $num_subs\n");

    🦛

Re: help declaring variables within perl one-liner
by ikegami (Pope) on Apr 01, 2021 at 19:48 UTC

    `...` is equilvalent to readpipe(qq`...`) (basically readpipe("...")), which is to say it interpolates. As such, you wanted the following:

    my $num_subs = `perl -w -0777 -pi -e 'my \$c=s/foo/bar/g;print STDOUT +"\$c\\n"' ./foo_test`; die("Can't execute child: $!\n" if $? == -1' die("Child killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F; die("Child exited with error ".( $? >> 8 )."\n") if $? >> 8; chomp($num_subs);
    The program can be simplified.
    my $num_subs = `perl -i -0777wpe'print STDOUT s/foo/bar/g' foo_test`; die("Can't execute child: $!\n" if $? == -1' die("Child killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F; die("Child exited with error ".( $? >> 8 )."\n") if $? >> 8;

    An approach that would make it easier to escape (if we still needed to do so):

    use String::ShellQuote qw( shell_quote ); my $cmd = shell_quote("perl", "-i", "-0777wpe", 'print STDOUT s/foo/ba +r/g', 'foo_test'); my $num_subs = `$cmd`; die("Can't execute child: $!\n" if $? == -1' die("Child killed by signal ".( $? & 0x7F )."\n") if $? & 0x7F; die("Child exited with error ".( $? >> 8 )."\n") if $? >> 8;

    Why are we even invoking a shell?

    use IPC::System::Simple qw( capturex ); # Core module my @cmd = ( 'perl', '-i', '-0777wpe', 'print STDOUT s/foo/bar/g', 'foo +_test' ); my $num_subs = capturex(@cmd); die("Child exited with error ".( $? >> 8 )."\n") if $? >> 8;

    Now, one would also quite reasonably argue that one shouldn't execute perl either. However, -i is quite good at avoiding data loss in the event of an error[1], and recreating this wouldn't be trivial.

    Finally, why execute a new perl at all?

    my $num_subs = 0; { local @ARGV = "foo_test"; local $^I = ""; local $/; $num_subs += s/foo/bar/g while <>; }

    As an added bonus, this last version handles the file not being found better. It also handles multiple files in @ARGV.


    1. This is a recent improvement.

    Seeking work! You can reach me at ikegami@adaelis.com

      Now, one would also quite reasonably argue that one shouldn't execute perl either. However, -i is quite good at avoiding data loss (due to recent improvements), and recreating this wouldn't be trivial.

      $^I

        For some reason, I thought you could only use that once per program. That's not true.

        $ printf 'foo\nbar\n' >file $ perl -e' for (1..2) { local @ARGV = qw( file ); local $^I = ""; print ">$_" while <>; } ' $ cat file >>foo >>bar

        Seeking work! You can reach me at ikegami@adaelis.com

Re: help declaring variables within perl one-liner
by haukex (Bishop) on Apr 01, 2021 at 20:08 UTC
    perl one-liners called from another perl script

    You've already gotten answers to your question, so let me add: Don't do that! It is hardly ever necessary unless you're doing really fancy stuff, and what you showed here does not fall in that category. See my node here for various examples of editing files and overwriting the originals.

      >perl one-liners scripts called from another perl script

      One liners are bad enough; but I swear, "shelling-out" to actual Perl scripts from a Perl script is one of the most offensive things I can see in Perl code. All the wasted cycles, dead electrons, PIDs, (oh the horror!) and server fan RPMs that are consumed for no reason ... incidentally in most (but not all) a simple conversion of the called script to a modulino would; then using it as a library rather than a system call would fix all of that. The only exception I've run across is if the called Perl script is specifically being used for sandboxing necessary privilege escallation; in that case, I think it's fine to treat the Perl script as an opaque userland program.

        One liners are bad enough; but I swear, "shelling-out" to actual Perl scripts from a Perl script is one of the most offensive things I can see in Perl code.

        My opinion is actually the other way around: system, when used properly, is less bad than using qx//, because the latter almost always involves the shell and introduces quoting problems - though what I said above still stands, calling another perl is almost never necessary (the most common exception I make is during testing). See Calling External Commands More Safely and The problem of "the" default shell.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11130676]
Approved by marto
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (5)
As of 2021-04-18 18:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?