Use B::Deparse to see what perl thinks the code is.

We used B::Deparse in Item 7. Know which values are false and test them accordingly, but we didn’t say much about that module. The B namespace has many modules that do various nasty black magic things with the perl parse tree.

We showed the example of the default behavior behind while reading from the diamond operator:

% perl -MO=Deparse -e 'while( <STDIN> ) { print "$. $_" }'

When running under B::Deparse, perl compiles the code then writes it out again instead of running it. Since it decompiles the Perl code, it comes back as perl thinks it is, which might not be how you think it is.

while (defined($_ = <STDIN>)) {
    print "$. $_";
}
-e syntax OK

If you run across a tricky one-liner, you can figure out what it is doing in the same way:

% perl -MO=Deparse -pi.old -e 's/foo/bar/g'

Now you see what perl wraps around that argument to -e:

BEGIN { $^I = ".old"; }
LINE: while (defined($_ = <ARGV>)) {
    s/foo/bar/g;
}
continue {
    print $_;
}
-e syntax OK

Don’t remember what the -F does and your cat accidently deleted your copy of perlrun?

% perl -MO=Deparse -naF: -e 'print @F[2,4,3]' /etc/passwd
LINE: while (defined($_ = <ARGV>)) {
    our(@F) = split(/:/, $_, 0);
    print @F[2, 4, 3];
}
-e syntax OK

You can do the same thing to ensure that the code that you think you are giving to perl is the same thing as what perl thinks it really is in the odd case where the syntax is blocking your view. By adding the -p switch to Deparse, you get parentheses Does exponentiation goes left-to-right or the other way around? Let B::Deparse tell you:

$ perl -MO=Deparse,-p -e 'print 4**$a**3 + 1'
print(((4 ** ($a ** 3)) + 1));
-e syntax OK

B::Deparse works well for obfuscations, too. Here’s a JAPH that uses only punctuation characters:

`$=`;$_=\%!;($_)=/(.)/;$==++$|;($.,$/,$,,$\,$",$;,$^,$#,$~,$*,$:,@%)=(
$!=~/(.)(.).(.)(.)(.)(.)..(.)(.)(.)..(.)......(.)/,$"),$=++;$.++;$.++;
$_++;$_++;($_,$\,$,)=($~.$"."$;$/$%[$?]$_$\$,$:$%[$?]",$"&$~,$#,);$,++
;$,++;$^|=$";`$_$\$,$/$:$;$~$*$%[$?]$.$~$*${#}$%[$?]$;$\$"$^$~$*.>&$=`

You can make it’s work a bit more apparent by letting B::Deparse reformat it for you (although Perl::Tidy (Item 111) can do a similar job):

`$=`;
use Errno ();
$_ = \%!;
($_) = /(.)/;
$= = ++$|;
($., $/, $,, $\, $", $;, $^, $#, $~, $*, $:, @%) = ($! =~ /(.)(.).(.)(.)(.)(.)..(.)(.)(.)..(.)......(.)/, $"), ++$=;
++$.;
++$.;
++$_;
++$_;
($_, $\, $,) = ($~ . $" . "$;$/$%[$?]$_$\$,$:$%[$?]", $" & $~, $#);
++$,;
++$,;
$^ |= $";
`$_$\$,$/$:$;$~$*$%[$?]$.$~$*$#$%[$?]$;$\$"$^$~$*.>&$=`;
punct syntax OK

Or how about this camel JAPH? Can you tell how it does its work?

                                                   sub j(\$){($
                     P,$V)=                      @_;while($$P=~s:^
                 ([()])::x){                    $V+=('('eq$1)?-32:31
           }$V+=ord(  substr(                 $$P,0,1,""))-74} sub a{
          my($I,$K,$  J,$L)=@_               ;$I=int($I*$M/$Z);$K=int(
         $K*$M/$Z);$J=int($J*$M             /$Z);$L=int($L*$M/$Z); $G=$
         J-$I;$F=$L-$K;$E=(abs($          G)>=abs($F))?$G:$F;($E<0) and($
          I,$K)=($J,$L);$E||=.01       ;for($i=0;$i<=abs$E;$i++ ){ $D->{$K
                  +int($i*$F/$E)      }->{$I+int($i*$G/$E)}=1}}sub p{$D={};$
                 Z=$z||.01;map{    $H=$_;$I=$N=j$H;$K=$O=j$H;while($H){$q=ord
                substr($H,0,1,"" );if(42==$q){$J=j$H;$L=j$H}else{$q-=43;$L =$q
              %9;$J=($q-$L)/9;$L=$q-9*$J-4;$J-=4}$J+=$I;$L+=$K;a($I,$K,$J,$ L);
              ($I,$K)=($J,$L)}a($I,$K,$N,$O)}@_;my$T;map{$y=$_;map{ $T.=$D->{$y}
              ->{$_}?$\:' '}(-59..59);$T.="\n"}(-23..23);print"\e[H$T"}$w= eval{
              require Win32::Console::ANSI};$b=$w?'1;7;':"";($j,$u,$s,$t,$a,$n,$o
              ,$h,$c,$k,$p,$e,$r,$l,$C)=split/}/,'Tw*JSK8IAg*PJ[*J@wR}*JR]*QJ[*J'.
               'BA*JQK8I*JC}KUz]BAIJT]*QJ[R?-R[e]\RI'.'}Tn*JQ]wRAI*JDnR8QAU}wT8KT'.
               ']n*JEI*EJR*QJ]*JR*DJ@IQ[}*JSe*JD[n]*JPe*'.'JBI/KI}T8@?PcdnfgVCBRcP'.
                '?ABKV]]}*JWe*JD[n]*JPe*JC?8B*JE};Vq*OJQ/IP['.'wQ}*JWeOe{n*EERk8;'.
                  'J*JC}/U*OJd[OI@*BJ*JXn*J>w]U}CWq*OJc8KJ?O[e]U/T*QJP?}*JSe*JCnTe'.
                   'QIAKJR}*JV]wRAI*J?}T]*RJcJI[\]3;U]Uq*PM[wV]W]WCT*DM*SJ'.  'ZP[Z'.
                      'PZa[\]UKVgogK9K*QJ[\]n[RI@*EH@IddR[Q[]T]T]T3o[dk*JE'.  '[Z\U'.
                        '{T]*JPKTKK]*OJ[QIO[PIQIO[[gUKU\k*JE+J+J5R5AI*EJ00'.  'BCB*'.
                             'DMKKJIR[Q+*EJ0*EK';sub h{$\ = qw(% & @ x)[int    rand
                              4];map{printf  "\e[$b;%dm",int(rand 6)+101-60*   ($w
                               ||0);system(  "cls")if$w ;($A,$S)=    ($_[1],   $
                                _[0]);($M,   @,)= split  '}';for(     $z=256
                                ;$z>0; $z   -=$S){$S*=   $A;p @,}      sleep$_
                                [2];while   ($_[3]&&($    z+=$ S)       <=256){
                                p@,}}("".   "32}7D$j"     ."}AG".       "$u}OG"
                                ."$s}WG"    ."$t",""      ."24}("        ."IJ$a"
                                ."}1G$n"    ."}CO$o"     ."}GG$t"        ."}QC"
                                 ."$h}"      ."^G$e"    ."})IG"          ."$r",
                                 "32}?"       ."H$p}FG$e}QG$r".          "}ZC"
                                 ."$l",          "28}(LC" .""            ."".
                                 "$h}:"           ."J$a}EG".             "$c"
                                 ."}M"             ."C$k}ZG".            "$e"
                                 ."}"             ."dG$r","18"          ."}("
                                ."D;"            ."$C"  )}{h(16         ,1,1,0
                               );h(8,          .98,0,0   );h(16         ,1,1,1)
                               ;h(8.0         ,0.98,0,     1);         redo}###
                             #written                                 060204 by
                           #liverpole                                  @@@@@@@
                        #@@@@@@@@@@@

It looks a little better after B::Deparse:

sub j (\$) {
    ($P, $V) = @_;
    while ($$P =~ s/^
                 ([()])//x) {
        $V += '(' eq $1 ? -32 : 31;
    }
    $V += ord(substr $$P, 0, 1, '') - 74;
}
sub a {
    my($I, $K, $J, $L) = @_;
    $I = int $I * $M / $Z;
    $K = int $K * $M / $Z;
    $J = int $J * $M / $Z;
    $L = int $L * $M / $Z;
    $G = $J - $I;
    $F = $L - $K;
    $E = abs $G >= abs $F ? $G : $F;
    ($I, $K) = ($J, $L) if $E < 0;
    $E ||= 0.01;
    for ($i = 0; $i <= abs $E; ++$i) {
        $$D{$K + int($i * $F / $E)}{$I + int($i * $G / $E)} = 1;
    }
}
sub p {
    $D = {};
    $Z = $z || 0.01;
    map {$H = $_;
    $I = $N = j($H);
    $K = $O = j($H);
    while ($H) {
        $q = ord substr($H, 0, 1, '');
        if (42 == $q) {
            $J = j($H);
            $L = j($H);
        }
        else {
            $q -= 43;
            $L = $q % 9;
            $J = ($q - $L) / 9;
            $L = $q - 9 * $J - 4;
            $J -= 4;
        }
        $J += $I;
        $L += $K;
        a $I, $K, $J, $L;
        ($I, $K) = ($J, $L);
    }
    a $I, $K, $N, $O;} @_;
    my $T;
    map {$y = $_;
    map {$T .= $$D{$y}{$_} ? $\ : ' ';} -59..59;
    $T .= "\n";} -23..23;
    print "\e[H$T";
}
$w = eval {
    do {
        require Win32::Console::ANSI
    }
};
$b = $w ? '1;7;' : '';
($j, $u, $s, $t, $a, $n, $o, $h, $c, $k, $p, $e, $r, $l, $C) = split(/}/, 'Tw*JSK8IAg*PJ[*J@wR}*JR]*QJ[*JBA*JQK8I*JC}KUz]BAIJT]*QJ[R?-R[e]\\RI}Tn*JQ]wRAI*JDnR8QAU}wT8KT]n*JEI*EJR*QJ]*JR*DJ@IQ[}*JSe*JD[n]*JPe*JBI/KI}T8@?PcdnfgVCBRcP?ABKV]]}*JWe*JD[n]*JPe*JC?8B*JE};Vq*OJQ/IP[wQ}*JWeOe{n*EERk8;J*JC}/U*OJd[OI@*BJ*JXn*J>w]U}CWq*OJc8KJ?O[e]U/T*QJP?}*JSe*JCnTeQIAKJR}*JV]wRAI*J?}T]*RJcJI[\\]3;U]Uq*PM[wV]W]WCT*DM*SJZP[ZPZa[\\]UKVgogK9K*QJ[\\]n[RI@*EH@IddR[Q[]T]T]T3o[dk*JE[Z\\U{T]*JPKTKK]*OJ[QIO[PIQIO[[gUKU\\k*JE+J+J5R5AI*EJ00BCB*DMKKJIR[Q+*EJ0*EK', 16);
sub h {
    $\ = ('%', '&', '@', 'x')[int rand 4];
    map {printf "\e[$b;%dm", int(rand 6) + 101 - 60 * ($w || 0);
    system 'cls' if $w;
    ($A, $S) = ($_[1], $_[0]);
    ($M, @,) = split(/}/, $_, 0);
    for ($z = 256; $z > 0; $z -= $S) {
        $S *= $A;
        p @,;
    }
    sleep $_[2];
    while ($_[3] and ($z += $S) <= 256) {
        p @,;
    }} '' . "32}7D$j" . '}AG' . "$u}OG" . "$s}WG" . "$t", '24}(' . "IJ$a" . "}1G$n" . "}CO$o" . "}GG$t" . '}QC' . "$h}" . "^G$e" . '})IG' . "$r", '32}?' . "H$p}FG$e}QG$r" . '}ZC' . "$l", '28}(LC' . "$h}:" . "J$a}EG" . "$c" . '}M' . "C$k}ZG" . "$e" . '}' . "dG$r", '18}(D;' . "$C";
}
{
    h 16, 1, 1, 0;
    h 8, 0.98, 0, 0;
    h 16, 1, 1, 1;
    h 8, 0.98, 0, 1;
    redo;
}

This is one of the reasons that Perl code obfuscators are a waste of time. If someone can run the code, they can get it back out.