++ 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. |