How does this Perl code work? - perl

I found this Perl program:
''=~('(?{'.(']])#+}'^'-/#._]').'"'.('/<[*-_<+>?}{>]#}+#}]])#+}#<[*-_<+>?}{>]#^'^'`^=_^<]_[[]+[/,]_/]-/#._]/^=_^<]_[[]+[/,|').',$/})')
It prints "Obfuscated Perl to print obfuscated Perl"
I want to know how it actually prints this.

It is making good use of the bitwise string XOR operator ^.
']])#+}' ^ '-/#._]'
evaluates to print,
'/<[*-_<+>?}{>]#}+#}]])#+}#<[*-_<+>?}{>]#^'
^ '`^=_^<]_[[]+[/,]_/]-/#._]/^=_^<]_[[]+[/,|'
evaluates to Obfuscated Perl to print obfuscated Perl" and the whole program reduces to
$ perl -MO=Deparse ...
'' =~ m[(?{print "Obfuscated Perl to print obfuscated Perl",$/})];
... syntax OK
Related: Acme::EyeDrops

Related

Difference between eof with and without parentheses? (Perl5)

I'm trying to make a perl one-liner mimic awk's file-relative line number counter "FNR". In itself this is not a problem. In one attempt, I used the following command:
perl -lnE 'say "$. : $_"; $.=0 if eof' testfiles
This works: the (automatically incremented) cross-file line counter $. is reset upon reaching the last line in each of the testfiles. When I add parentheses to the eof function call, like so:
perl -lnE 'say "$. : $_"; $.=0 if eof()' testfiles
the program doesn't work anymore as intended, since eof() only returns true when processing the last line of the last of the testfiles. This is properly documented here.
My question is: How does eof know if it is called with or without (), and thus what to do? Is the function somehow special-cased, or can any function find out if it is called with or without parentheses?
It's an operator whose semantics can't be replicated by subs.
Just like + and and, functions in perlfunc are operators.
The functions in this section can serve as terms in an expression. They fall into two major categories: list operators and named unary operators.
As operators, they can provide any syntax they please.
Prototypes can be used to replicate the syntax of some of the functions. You can determine which using prototype("CORE::funcname").
$ perl -M5.010 -e'say prototype("CORE::length") // "[undef]"'
_
$ perl -M5.010 -e'say prototype("CORE::sysread") // "[undef]"'
*\$$;$
$ perl -M5.010 -e'say prototype("CORE::say") // "[undef]"'
[undef]
Technically, eof's syntax can be replicated by subs. So prototype does return a value for eof.
$ perl -M5.010 -e'say prototype("CORE::eof") // "[undef]"'
;*
Its semantics can't be replicated, however. There's no way for a sub to tell if it was called with or without parens. Well, it might be possible using Devel::CallParser or the keyword plugin, but this would require C code, would require a lot work, and they have problems of their own.
As a side note, open's syntax is misreported as being able to be reproduced.
$ perl -M5.010 -e'say prototype("CORE::open") // "[undef]"'
*;$#
Built-in unary functions are special-cased in the parser. There's no perl-level way to do the same for subroutine calls.

perl: upper case evaluation for subroutine

I need to pass system variable in upper case to perl subroutine.
For example, if the variable with name VARNAME (value 'super'), i need to pass "SUPER_MAN".
In general, if we use 'uc' option like in the example below, we can convert to upper case
perl -e 'print uc"$ENV{VARNAME}\n"'
But when we try to pass it in subroutine, we need to include uc function in the syntax and evaluate during runtime. To emulate that I was trying the below but not working, Where am I going wrong?
perl -e 'print ".uc($ENV{VARNAME})_MAN\n"'
.uc(super)_MAN
Alternate methods/approach is also welcome.
Take the uc out of the quotes "", since perl thinks you want the literal letters uc:
FOO=abc perl -e 'print "." . uc($ENV{FOO}) . "_MAN\n"'
.ABC_MAN
perldoc perlop - Quote and Quote like Operators

Getting a Unicode code point ("character") by name

Perl makes it very convenient to get some symbol.
$ perl -CO -E 'say "\N{FAMILY}"'
👪
Is there a way to evaluate the symbol with "\N{}" via interpolation? Something similar to:
perl -CO -E 'my $what = "FAMILY"; say "\N{$what}"'
(which does not work, but gives you the flavour).
Besides feasibility, is there any drawback or possible threat in such evaluation? E.g. say the $what string is user-defined, I would never eval $what. Would I \N{$what} safely?
Ops...
After getting the answers, I noticed how this information clearly stated in perlunicook. Sorry for the lack of RTFM. I guess this is a good thing to have on stackoverflow anyway.
Use the vianame function from charnames:
perl -CO -Mcharnames=:full -wE 'say chr charnames::vianame("PILE OF POO")'
💩
use charnames ();
my $what = "FAMILY";
say charnames::string_vianame($what);

print doesn't recognize barewords as parameter?

In non-strict mode of Perl, barewords could be recognized as string, like below:
$x = hello;
print $x;
But it seems barewords cannot be passed to print directly, like below one doesn't output the string. Why are they different?
print hello;
In cases like this, the B::Deparse module can be very helpful:
$ perl -MO=Deparse -e 'print hello;'
print hello $_;
-e syntax OK
As you see, it interprets the identifier as a filehandle and takes the value to be printed from $_.
Barewords should be avoided like the plague they are. Personally, I'll continue to avoid them all the time. They're a relic left over from the wild west days of Perl (circa 1990) and would have been eliminated from the language except for the need to maintain backwards compatibility.
In any case, in that context, it prints $_ to the file handle hello, doesn't it? That's the sort of reason why barewords are worth avoiding. (Compare: print STDERR "Hello\n"; and print STDERR;, and print hello;).
For example:
open hello, ">junk.out";
while (<>)
{
print STDERR "Hello\n";
print STDERR;
print hello;
}
Sample run:
$ perl hello.pl
abc
Hello
abc
def
Hello
def
$ cat junk.out
abc
def
$
In case it isn't clear, I typed one line of abc, which was followed by Hello and abc on standard error; then I typed def, which was followed by Hello and def on standard error. I typed Control-D to indicate EOF, and showed the contents of the file junk.out (which didn't exist before I ran the script). It contained the two lines that I'd typed.
So, don't use barewords — they're confusing. And do use use strict; and use warnings; so that you have less opportunity to be confused.
An identifier is only a bareword if it has no other meaning.
A word that has no other interpretation in the grammar will be treated as if it were a quoted string. These are known as "barewords".
So, for example, there are no barewords in the following program:
sub f { print STDOUT "f()\n"; }
X: f;
In other circumstances, all of sub, f, print, STDOUT and X could be barewords, but they all have other meanings here. I could add use strict;, and it'll still work fine.
In your code, you used print hello as I used print STDOUT. If an identifier follows print, you are using the print FILEHANDLE LIST syntax of print, where the identifier is the name of a file handle.

Why perl parses regex

Perl misses my DWIM (ternary ?:) and forces regex.
perl -e "print $bool ?'T' :'F'"
Use of ?PATTERN? without explicit operator is deprecated at -e line 1.
Search pattern not terminated or ternary operator parsed as search pattern at -e
line 1.
Why is that so?
Following two examples have correctly parsed ternary operator,
perl -e "print $bool ? 'T' :'F'"
perl -e "print [] ?'T' :'F'"
This is on windows, but almost same thing on *nix
perl -e 'print $bool ?"T" :"F"'
so it doesn't look like shell related.
The ?...? is an operator, as seen here, and the problem here is one of ambiguity. The compiler does not know if it is seeing a pattern or a ternary operator (as the subsequent warning says), so it guesses, and guesses wrong.
I think it has something to do with print() setting it up for a fall, in that print() has the syntax:
print FILEHANDLE LIST
So, it pre-determines $bool as a file handle, and ?'.... as a statement. Then it notices that $bool is not a file handle, but the ternary is already cast in its faulty role. You will notice that the following works as intended:
perl -e"print STDOUT $bool ?'t' :'f'"
Perl syntax has ambiguities.
print $bool ?...
can be parsed as
print FILEHANDLE LIST
+----------------- print
| +----------- FILEHANDLE
| | +------ LIST (A regex match ?...?)
| | |
_____ _____ ____
print $bool ?...
or as
print LIST
+----------------- print
| +--------- LIST (A conditional operator ...?...:...)
| |
_____ __________
print $bool ?...
Without looking too far ahead, perl incorrectly guesses that you meant the first one, but lets you know that it guessed.
Search pattern not terminated or ternary operator parsed as search pattern
Workarounds:
Adding a space after the ? (print $bool ? 'T' :'F';) makes it guess correctly (since it no longer looks as much like ?...?).
Adding a + before the file handle (print +$bool ?'T' :'F';) makes it guess correctly (since it's no longer a valid file handle expression).
Remove the space before the ? (print $bool?'T' :'F';) makes it guess correctly (since it's no longer a valid file handle expression).
Adding a file handle (print STDOUT $bool ?'T' :'F';) makes it guess correctly (since $bool must now be the start of the argument list).
Are you on Windows or on *nix?
On linux I get no warnings when I switch " and ' ( perl -e 'print $bool ? "T" :"F"'). The way you wrote it the $bool is interpolated by the shell, to an empty string, and your code becomes perl -e "print ?'T' :'F'" which correctly triggers the warning.