http://www.perlmonks.org?node_id=1014860

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

&main; sub main() { my @mylist = qw(a, b, c); foreach(@mylist) { &fun($_); print "changed:\$_ $_\n"; } } sub fun() { my $ret = open(STATUS, "echo d-e-f|"); while(<STATUS>){ if(/d-e-f/){ close(STATUS); return; } } close(STATUS); } output not a,b,c: changed:$_ d-e-f changed:$_ d-e-f changed:$_ d-e-f

Why $_ is changed?? Anyone know that? I'm going to read something from the pipe.

Is there any safer/simple way to "system"/run a command and get its output into a variable?

Replies are listed 'Best First'.
Re: Very curious Problem...
by LanX (Saint) on Jan 23, 2013 at 09:06 UTC
    > Why $_ is changed??

    while(<STATUS>) sets $_ (see perlop ¹) and $_ is global.

    Maybe you rather wanna use:

     while (my $line = <STATUS>)

    Cheers Rolf

    UPDATE

    ¹)

    The following lines are equivalent:

    while (defined($_ = <STDIN>)) { print; } while ($_ = <STDIN>) { print; } while (<STDIN>) { print; } for (;<STDIN>;) { print; } print while defined($_ = <STDIN>); print while ($_ = <STDIN>); print while <STDIN>;
      Is it always unsafe when return back to the "upper" level loop in perl, since the inner looper may probably change the global $_?
        Yes after doing complicated things like invoking a subroutine $_ is pretty unsafe. ¹)

        Not only subs, there are plenty of commands which have a side effect on $_.

        $_ is only a shortcut with limited use in small situations that you can completely control, but over time code grows in unexpected ways.

        Better use named lexical variables in production code, especially when it is maintained by various people. ²)

        Global variables are generally more critical and should only be use for good reasons.

        I can strongly recommend you getting a copy of Perl Best Practices for good discussions of such issues. It will certainly improve your Perl skills.

        Cheers Rolf

        ¹) for completeness you could use local $_ (or even my $_ in recent Perls) within the subroutine, but this doesn't really solve the general problem of $_'s limited radius.

        ²) Think of $_ as the word it in human language, the longer a conversation only using "it", the unclearer what it (sic) is supposed to mean.

        if your sub uses $_ you should  local $_;
      thank you both!
Re: Very curious Problem...
by vinoth.ree (Monsignor) on Jan 23, 2013 at 09:09 UTC
    Is there any safer/simple way to "system"/run a command and get its output into a variable?

    Use backtick (``) to run a command and get the output into a variable.

    Update:

    Adding Additional info.

    I always use "qx" which is another form for backticks, which in perl will capture the return from the system call.

    OR

    Even check for capture and capturex methods in IPC::System::Simple module

Re: Very curious Problem...
by muba (Priest) on Jan 23, 2013 at 12:06 UTC

    ++ for demonstrating the problem in a simple self-contained piece of code. One advantage of reducing a problem to the smallest set of instructions that replicates the problem, is that it allows you to debug it using your Eyeball Mk I (and maybe a pencil and a piece of paper).

    Using your sample code, let's Eyeball Debug it, starting at the top and following along with the flow of the code.

    I don't know how you pronounce $_ — maybe you call it "dollar underscore" whatever, but I call it "it". That's right. So if ($_ == 3), to me, reads "if it equals 3", and @foo = grep { defined $_ } @bar; I would pronounce as "at-foo now is ... grep-if it is-defined ... from at-bar".

    Why do I share this? Because I feel it really helps to think of $_ as being "it". Let's go:

    STEP CODE MEANING + IT IS NOW... 1 &main(); call 'main', no arguments + undef 2 my @mylist = qw(a b c); create and define @mylist + undef 3 foreach(@mylist) iterate over @mylist + undef 3a iteration 1 + it is now "a" 4 &fun($_); &fun(it) + "a" 5 open(STATUS, "echo d-e-f|"); echo "d-e-f" into new fh STATU +S "a" 6 while (<STATUS>) sequentially read lines + "a" 6a iteration 1: read it. + it is now "d-e-f" 7 if (/d-e-f/) if it matches (and it does), + "d-e-f" 8 close(STATUS) close the FH + "d-e-f" 9 return; go back to whence we came + "d-e-f" 10 print "changed: \$_ $_\n" print it + "d-e-f" 3b iteration 2 + it is now "b" ...snip...

    This clearly shows where, how, and why it (or dollar-underscore) got changed.

    Solutions have been given elsewhere in this thread. I just wanted to show you how I would've debugged it. The code, that is, not the value of $_. Or maybe both.

      Very interesting! Thanks for the share.
Re: Very curious Problem...
by CountZero (Bishop) on Jan 24, 2013 at 07:31 UTC
    May I comment that in general you probably do not want to define your subs with parentheses after the sub-name as this is the syntax for using prototypes. Neither do you want to call your subs with a pre-pended &. Not only is it old ("Perl 4") style, it has side-effects, such as disabling the prototype system.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics