NAME

pl - Perl One-Liner Magic Wand

SYNOPSIS

There are always those tasks too menial for dedicated script, but still too cumbersome even with the many neat one-liner options of perl -E. This small script fills the gap with many bells & whistles: Various one-letter commands & magic variables (with meaningful aliases too) and more nifty loop options take Perl programming to the command line. List::Util is fully imported. If you pass no program on the command line, starts a simple Perl Shell.

How to e(cho) values, including from @A(RGV), with single $q(uote) & double $Q(uote):

pl 'echo "${quote}Perl$quote", "$Quote@ARGV$Quote"' one liner
pl 'e "${q}Perl$q", "$Q@A$Q"' one liner

>   'Perl' "one liner"

Same for hard-to-print values:

pl 'echo \"Perl", \@ARGV, undef' one liner
pl 'e \"Perl", \@A, undef' one liner

>   \'Perl' [
>     'one',
>     'liner'
>   ] undef

Loop over args, printing each with line ending. And same, SHOUTING:

pl -opl '' Perl one liner
pl -opl '$_ = uc' Perl one liner

>   Perl
>   one
>   liner
>   PERL
>   ONE
>   LINER

Print up to 3 matching lines, resetting count (and $.) for each file:

pl -rP3 '/Perl.*one.*liner/' file*

Count hits in magic statistics hash %N(UMBER):

pl -n '++$NUMBER{$1} while /(Perl|one|liner)/g' file*
pl -n '++$N{$1} while /(Perl|one|liner)/g' file*

>          2: one
>          7: liner
>          9: Perl

Though they are sometimes slightly, sometimes quite a bit more complicated, most Perl one-liners from the internet work, just by omitting -e or -E (pl has only one main program, but you can just as well concatenate the -es with ;). See minor differences for exceptions. There are many varied examples.

DESCRIPTION

Don't believe everything you read on SourceForge^H^H^H the internet! -- Aristotle ;-)

Pl follows Perl's philosophy for one-liners: the one variable solely used in one-liners, @F, is single-lettered. Because not everyone may like that, Pl has it both ways. Everything is aliased both as a word and as a single letter, including Perl's own @F & *ARGV.

Perl one-liners, and hence pl, are by nature bilingual. You must run the command with its options & arguments, typically from Shell. By design, Perl quotes mimic Shell quotes, so here they collide. As Perl also uses Shell meta-characters like $, the best solution is to protect Perl-code from the Shell with single quotes. That means they can't be used inside. (An ugly way around that is '\'', which ends a string, blackslashes a quote and starts another. For literal quotes use $q(uote).) For quoting use double quotes or q{}.

DOCUMENTATION

Options

Many of perl's options are also available in pl, sometimes enhanced with extra functionality. And the new options complement what perl offers, specifically oriented towards one liners.

-0[octal]

perl: Specify record separator with -n/-p (\0, if no argument).

-a

perl: Autosplit mode with -n/-p (splits $_ into @F(IELD)).

-bprog

Run program before reading a new file in -n/-p.

-Bprog

Add program before main program in same scope. So you can use it to initialise my variables. Whereas, if you define a my variable in the main program of a -n, -p, -P, -o or -O loop, it's a new variable on each iteration. This doesn't do a BEGIN block, but this might be changed in a later version if program is wrapped in {}.

-c

perl: Check syntax only (runs BEGIN and CHECK blocks).

-C[number/list]

perl: Enables the listed Unicode features.

--color[=when]

Colorize some of the output; when can be never, always, or auto (the default).

-d[:debugger]

perl: Run program under debugger.

-D[number/list]

perl: Set debugging flags (argument is a bit mask or alphabets).

-eprog

Run program after finishing reading a file in -n/-p.

-Eprog

Add an END block after main-program in same scope. So my-vars work as follows: The END block, is a closure of the 1st $inner variable. Perl warns "Variable "$inner" will not stay shared":

pl -OB 'my $outer' -E 'echo $inner, $outer' 'my $inner = $outer = $ARGV' a b c
pl -OB 'my $outer' -E 'e $inner, $outer' 'my $inner = $outer = $A' a b c

>   a c
-f

perl: Don't do $sitelib/sitecustomize.pl at startup.

-F/pattern/

perl: Provide split() pattern for -a switch (//'s are optional).

-Idirectory

perl: Specify @INC/#include directory (several -I's allowed).

-i[extension]

perl: Edit <> files in place (makes backup if extension supplied).

-n

perl: Assume while (<>) { ... } loop around program. It's a little richer than that: if you use last, it closes the current file, leaving you to continue the loop on the next file.

-O

Assume for $ARGV (@ARGV) { ... } loop around main program. In this case -p doesn't imply -n.

-o

Assume for(@ARGV) { ... } loop around main program. In this case -p doesn't imply -n.

-p[number]

Does pl -penis do pussy? It implements cat. :-*

perl++: On each loop print (also -o/-O) iteration. If number is given, print at most number times.

-P[number]

Like -p, but print only if main program evaluates to true, like grep.

-r

Reset $. and -p/-P counter for each file.

-T

perl: Enable tainting checks.

-t

perl: Enable tainting warnings.

-U

perl: Allow unsafe operations.

-u

perl: Dump core after parsing program.

-v

perl: Print version, patchlevel and license.

-VVERSION

Rerun with given perl version, which is just a string appended to perl.

-W

perl: Enable all warnings.

-w

perl: Enable many useful warnings.

-X

perl: Disable all warnings.

Functions

There are various functions, always also with a one letter alias, which perform little tasks that can be useful in one liners.

benchmark { } [name[, arg...]] | b { } [name[, arg...]]

Benchmark slow code for 10s, display name, looping over args.

Benchmark { } [name[, arg...]] | B { } [name[, arg...]]

Same, but run code 100 times in benchmark, to reduce overhead.

Config [regexp...] | C [regexp...]

Import and return %Config, e.g. Config->{sitelib}, optionally only part matching regexps.

Date [arg...][, tz] | D [arg...][, tz]

Why is Halloween Christmas? Because Oct. 31 = Dec. 25. ;-)

Date (from arg [s, us], s{.us}, offset [+-]s{.us}, tz ([+-]0-14{:mm|.ff}). Microseconds should be passed as strings, because floats have implementation-dependent rounding issues. Positive offsets must be passed as strings, because otherwise the + gets lost. Returns the date, if called in some context, else echoes it.

pl 'Date; $_ = Date -86400, "+3600"; e $_, " -- ", Date "+8:45"'
pl 'D; $_ = D -86400, "+3600"; e $_, " -- ", D "+8:45"'

>   Wed Sep  1 00:25:17.583847 2021
>   Tue Aug 31 01:25:17.583923 2021  --  Wed Sep  1 07:10:17.583959 +08:45 2021
echo [arg...] | e [arg...]

Echo prettified args or $_ with spaces and newline. Prettified means, undef becomes that string, italic if --color is active. Anything that can be stringified, is. Any other reference goes through Data::Dumper, which is loaded only if needed.

If it is called in scalar context (e.g. $x = echo ...) instead return the same as it would echo, in one string (inspired by Shell $(...)). If it is called in list context (e.g. @l = echo ...) return each arg prettified individually, with a newline on the last one.

Echo [arg...] | E [arg...]

Same, but no newline.

form format, [arg...] | f format, [arg...]

Form(at) and echo prettified args or $_ with newline. If it is called in scalar or list context (e.g. $x = form ...) instead return the same as it would echo, in one string.

Form format, [arg...] | F format, [arg...]

Same, but no newline.

Isodate [arg...][, tz] | I [arg...][, tz]

Same as D(ate), but uses ISO format.

pl 'Isodate; $_ = Isodate 7 * -86400; e $_, " -- ", Isodate "+8.75"'
pl 'I; $_ = I 7 * -86400; e $_, " -- ", I "+8.75"'

>   2021-09-01T00:25:17.606616
>   2021-08-25T00:25:17.606698  --  2021-09-01T07:10:17.606730 +08:45
keydiff [key[, value]] | k [key[, value]]

Store value or chomped $_ in $KEYDIFF{key or $1}[$ARGIND]. At the END for each key (sorted numerically if possible) all values are diffed.

Keydiff [number[, value]] | K [number[, value]]

Same, but key is $FIELD[number] or $F[0].

Number [n] | N [n]

Trim %N(UMBER) values less than n (default 2) e.g.; -ENumber or -E 'N 5'.

piped { } cmd[, arg...] | p { } cmd[, arg...]

Open pipe from cmd and loop over it.

Variables

There are various variables, always also with a one letter alias, many of which perform magic tasks at the END.

*ARGV | *A

perl: ARGV, $ARGV & @ARGV are all aliased to A, $A & @A.

$ARGIND | $I

Index of ARG currently being processed in -o, -n or -p.

@FIELD | @F

perl: This is an alias to loop autosplit variable @F.

$quote | $q

Predefined to a single quote '. There is no magic associated. Perl's q() makes it easy to integrate functional quotes under all circumstances. This does the same for literal quotes.

$Quote | $Q

Predefined to a double quote ". There is no magic associated. Perl's qq() makes it easy to integrate functional quotes under all circumstances. This does the same for literal quotes.

%KEYDIFF | %K

At END, sort by keys, print keydiff of $ARGIND array elements. Filled by keydiff.

%NUMBER | %N

At END, sort numerically by values.

*RESULT | *R

At END, echo $RESULT if defined, then @RESULT one per line if not empty, then %RESULT sorted by keys.

$ENV{PLDUMP}

Since pl -MO=Deparse won't show your parts of the program, it can be quite baffling when things go wrong. If you export this before starting pl, you see how your parts get embedded in various bits of generated stuff. If you install perltidy, it will be used.

You will not see the effect of -a, -F or -n as those get wrapped around it by perl behind the scenes. Due to interfacing with those options and providing for various option-combinations, the generated code may be too complicated. Expect it to change and be optimized further in the future:

PLDUMP=1 \
    pl 'say "Hello Perld!"'

>   sub pl::prog {
>       $pl::last = 1;
>     LINE: {
>   #line 1 "main program"
>           say "Hello Perld!";
>       } continue {
>           $pl::last = 0;
>       }
>       if ( $pl::last || eof ) {
>           ++$ARGIND;
>           if ($pl::last) { my $d = $.; close ARGV; $. = $d }
>           exit if $pl::last == 2;
>       }
>   }

COMPATIBILITY

Even if it is rare nowadays, Perl 5.10 is still found out in the wild. Pl tries to accomodate it gracefully, falling back to what works. It has shims for any, all, none, notall, product & sum0. Dumped data-structures may be formatted with a funny margin and h(osts) will find the less IPv6 resolutions, the older your Perl.

Minor Differences with perl -e

Known minor differences are:

  • don't goto LINE, but next LINE is fine

  • in a -n loop last is per file instead of behaving like exit

  • using pop, etc. to implicitly modify @A(RGV) works in -B BEGIN code, but not in your main program (which gets compiled to a function)

  • shenanigans with unbalanced braces won't work

Windows Notes

Work Is Never Done On Windows Systems ;-)

Do yourself a favour and get a real Shell, e.g. from WSL, Cygwin, MSYS, MinGW or git! If you can't avoid command.com or cmd.exe, you will have to first convert all inner quotes to qq. Then convert the outer single quotes to double quotes:

pl "echo qq{${quote}Perl$quote}, qq{$Quote@ARGV$Quote}" one liner
pl "e qq{${q}Perl$q}, qq{$Q@A$Q}" one liner

>   'Perl' "one liner"

Any help for getting this to work in PowerShell is welcome!

While the old Windows 10 terminal understands ANSI escape sequences, it makes it horribly hard to activate them. So they are off by default, requiring --color to override that choice.

Pl is maintained on SourceForge and also available on meta::cpan.