Some tasks are too menial for a dedicated script but still too cumbersome even with the many neat one-liner options of perl -E
. This small script fills the gap: various one-letter commands & magic variables (with meaningful aliases too) and more nifty loop options take Perl programming to the command line. Fully imports List::Util
. With no program on the command line, starts a pl Shell.
How to e
values, including from @A
, with single $q
& double $Q
:
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
:
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're sometimes slightly, sometimes quite a bit more complicated, most Perl one-liners from the internet work, just by omitting -e
or -E
. There's only one main program in pl, but you can just as well concatenate the -e
s with ;
. See minor differences for exceptions. Let's see many varied examples.
Don't believe everything you read on SourceForge the internet! –
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, shown here slightly lighter as shell '
. That means you can't use them inside. (An ugly way around that, is '\''
, which ends a string, backslashes a quote and starts another.) For literal quotes use $q
. For quoting use double quotes or q{}
.
Shell and Perl, unlike most other languages, don't make you stick your toe up your nose to get newlines into strings. Thus, you see long "one-liners" as legible many-liners. In the ☰ veggie-burger menu, you can toggle many-line display. In normal text short and long name variants are initial-bold as X
. All examples use the long names, if applicable. Those are in the darker blue upper half. In the lighter lower half, you get the short variant, again selectable in the menu. If there is a ▶ play button, hover (or tap) the example to fold out a terminal with the output. The blue sidelines show the structure and act as breadcrumbs: You can hover, click or bookmark them.
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]
-Aprog
Map program over already available @A
(from command line or previous -A) or undef. If you wrap program in {}
uses grep
instead of map
. The result becomes the new @A
. You can mix it with -B. The 1st two are equivalent, except that the 1st one isn't limited by Shell line length limitations. The third again greps by file size, reading only the Perl modules less than 1 kB:
pl -nA '<*.pm> ' '... '
pl -n '... ' *.pm
pl -nA '<*.pm> ' -A '{ (stat)[7] < 1000 } ' '... '
-a
-bprog
-Bprog
Add program before main program in same scope. 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, that's a new variable on each iteration. This doesn't do a BEGIN
block unless you wrap program in {}
. You may mix it with -A.
-c
-C[number/list]
--color[=when]
-d[:debugger]
-D[number/list]
-eprog
-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
-F/pattern/
-Idirectory
-i[extension]
As I said before, I never repeat myself. 😂
-n
-o[number]
Assume for(@ARGV) { … }
loop around main program, and $ARGIND
(or $I
) is the current position. In this case -p doesn't imply -n. If you give number, passes that many args at once as an array, referencing the original values. If there aren't enough on the last round, fills up @A
with undef
s.
pl -opl ' ' I II III IV
pl -o3 'echo $ARGIND, @$_ ' i ii iii iv v vi vii viii ix
pl -opl ' ' I II III IV
pl -o3 'e $I, @$_ ' i ii iii iv v vi vii viii ix
I II III IV 0 i ii iii 3 iv v vi 6 vii viii ix
-O[number]
Does pl -penis
do pussy? cat
.
-p[number]
-P[number]
-r
-T
-t
-U
-u
-v
-Vversion
-W
-w
-X
Functions
Various functions, always also with a one letter alias, perform little tasks that can be useful in one-liners.
benchmark { } [name[, arg…]]
b { } [name[, arg…]]
Benchmark { } [name[, arg…]]
B { } [name[, arg…]]
Config [regexp…]
C [regexp…]
Why is Halloween Christmas? Because Oct 31 = Dec 25. 🎅
Date [arg…][, tz]
D [arg…][, tz]
Date (from arg [s, us], s{.us}, offset [+-]s{.us}, tz ([+-]0-14{:mm|.ff}). You should pass microseconds as strings because floats have implementation-dependent rounding issues. You must pass positive offsets as strings because otherwise it loses the +
. Returns the date, if called in some context, else echoes it.
pl 'Date;
$_ = Date -86400, "+3600";
echo $_, " -- ", Date "+8:45" '
pl 'D;
$_ = D -86400, "+3600";
e $_, " -- ", D "+8:45" '
Mon Aug 14 23:34:20.425194 2023 Mon Aug 14 00:34:20.425292 2023 -- Tue Aug 15 06:19:20.425333 +08:45 2023
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 pl loads only if needed.
If it's called in scalar context (e.g., $x = echo …
) instead return the same as it would echo, in one string (inspired by Shell $(…)
). If it's called in list context (e.g., @l = echo …
) return each arg prettified individually, with a newline on the last one.
Echo [arg…]
E [arg…]
form format, [arg…]
f format, [arg…]
Form format, [arg…]
F format, [arg…]
Isodate [arg…][, tz]
I [arg…][, tz]
keydiff [key[, value]]
k [key[, value]]
Keydiff [number[, value]]
K [number[, value]]
Number [n[, hash_or_array]]
N [n[, hash_or_array]]
Trim hash_or_array (default %N
) values less than n
(default 2), or, if negative, more than -n
e.g., -EN
or -E 'Number -5, @RESULT'
. This happens recursively at any depth in nested hashes or arrays.
The first argument can also be a function, where deletion happens for every element where it returns a falsy value. It gets called for each scalar element with 3 arguments: 0 - the current (nested) hash or array, 1 - its key or index, 2 - its value.
pl '%RESULT = (neg => [-9..-1], pos => [1..9]); Number sub { ! ($_[2] % 3) }, %RESULT '
pl '%R = (neg => [-9..-1], pos => [1..9]); N sub { ! ($_[2] % 3) }, %R '
neg: [ -9, -6, -3 ] pos: [ 3, 6, 9 ]
piped { } cmd[, arg…]
p { } cmd[, arg…]
template [tmpl[, hash|key => value…]]
t [tmpl[, hash|key => value…]]
Replace values from hash in template (defaults to $_
), which may also be a filehandle of ref to a filename. Hash (defaults to %T
) may be given as a reference or key-value pairs. The template may use one of three markup styles: [% x %]
, {{ x }}
, or <?pl x ?>
. These are totally equivalent. The 1st one found in the template will be used. If you put a ~
just inside a delimiter (e.g., <?pl~ x ~?>
), it will gobble horizontal whitespace on that side, behind also one newline.
Within the markup x may be any valid syntax for a Perl hash key. The 3 characters |
, :
, or !
mark the end of the key name and introduce 3 kinds of filter. The 1st two, can be ?|
or ?:
, meaning this item applies only if the key exists. Otherwise the key is optional. If you give a key, its value is locally assigned to $_
. If undef
it defaults to ''
.
You write the filter in Perl. You can abort the filter with last
. If the filter starts with |
then you pipe its scalar value into the template. If there is no filter after |
, you recursively treat the value as a template. If the filter starts with :
then you insert the value of $_
. An easy way to only do that is [%:%]
. The whole template gets compiled to an anonymous sub
, the 1st time. As this happens inside the function, filters have no access to your surrounding my
variables.
You insert the Perl code following !
into the code the template compiles to verbatim. This is useful for flow control. E.g., if $TEMPLATE{x}
(or $T{x}
) is an array, you can loop over its values as @$_
. The loop logic is entirely in Perl, which again localizes $_
. If do-nothing [%!%]
starts the document, it "declares" its markup syntax. And with ~
it suppresses surrounding space including one following newline. (See PLDUMP)
pl 'template q(A: {{ a }} B: {{ | "no" }}{{ b ?| "yes" }} C: {{ lc "C" }} C+1: {{ c|$_ + 1 }}), qw(a 1 c 3) '
pl 'template q([%x!for( @$_ ) { %] X: [% : %] [%~ ! } %][% y %]), { x => [1..5] } '
pl 'template q(<ul><?pl l!for( @$_ ) { ?> <li><?pl: ?></li><?pl ! } ?> </ul>), { l => [1..3] } '
pl 't q(A: {{ a }} B: {{ | "no" }}{{ b ?| "yes" }} C: {{ lc "C" }} C+1: {{ c|$_ + 1 }}), qw(a 1 c 3) '
pl 't q([%x!for( @$_ ) { %] X: [% : %] [%~ ! } %][% y %]), { x => [1..5] } '
pl 't q(<ul><?pl l!for( @$_ ) { ?> <li><?pl: ?></li><?pl ! } ?> </ul>), { l => [1..3] } '
A: 1 B: no C: 3 C+1: 4 X: 1 X: 2 X: 3 X: 4 X: 5 <ul> <li>1</li> <li>2</li> <li>3</li> </ul>
Template [tmpl[, hash|key => value…]]
T [tmpl[, hash|key => value…]]
Same but no newlines, also not on nested templates.
pl '$_ = q( [ <?pl~inner| ~?> ]);
$TEMPLATE{inner} = q([ {{ who | $_ || "Jack" }} in the box ]);
Template;
$TEMPLATE{who} = "Sue";
template '
pl '$_ = q( [ <?pl~inner| ~?> ]);
$T{inner} = q([ {{ who | $_ || "Jack" }} in the box ]);
T;
$T{who} = "Sue";
t '
[[ Jack in the box ]] [[ Sue in the box ] ]
Variables
Various variables, always also with a one letter alias, often perform magic tasks at the END
.
*ARGV
*A
$ARGIND
$I
@FIELD
@F
$quote
$q
$Quote
$Q
%KEYDIFF
%K
%NUMBER
%N
*RESULT
*R
$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 with value 1 before starting pl, you see how your parts get embedded in various bits of generated stuff. If you install perltidy
, pl will use it. Some options get handled by perl, so they won't show up here:
PLDUMP=1 \
pl 'say "Hello Perld!" '
use feature ':' . substr $^V, 1; 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; } }
If you export this with value 2, it will instead show what a template would compile to:
PLDUMP=2 \
pl 'template q([%x!for( @$_ ) { %] X: [% : %] [%~!} %]), x => [1..5] '
PLDUMP=2 \
pl 't q([%x!for( @$_ ) { %] X: [% : %] [%~!} %]), x => [1..5] '
my @_template = [ '', ' X: ', '' ] ; #line 1 "template" sub { my $_template = $_template[0] ; for((local $_ = $TEMPLATE{x} // '')?():(), @$_ ) { ;$_template .= $_template[1] . (do {{ ; $_ }} // '');$_template .= $_template[2] ; } ; ; $_template }
COMPATIBILITY
Even if it's rare nowadays, you can still find Perl 5.16 out in the wild (e.g., in RHEL 7). Pl accommodates it gracefully, falling back to what works. It has shims for any
, all
, none
, notall
, product
& sum0
. (Some Unices maintain even older Perl versions, e.g., AIX or Solaris: you can go back till Perl 5.10 with pl 0.63.2.)
Minor Differences with perl -E
Known minor differences are:
by design in an -n loop
last
is per file instead of behaving likeexit
don't
goto LINE
, butnext LINE
is fineusing
pop
, etc. to implicitly modify@A
works in -B begin code but not in your main program (which gets compiled to a function)RGV shenanigans with unbalanced braces won't work
Work Is Never Done On Windows Systems 😉
Windows NotesDo yourself a favour and get a real Shell, e.g., from Cygwin, git, MinGW, MSYS, or WSL! If you can't avoid command.com or cmd.exe, you will have to first convert all inner quotes to qq
or \"
. Then convert the outer single quotes to double quotes:
pl "echo qq{${quote}Perl$quote}, \"$Quote@ARGV$Quote\" " one-liner
pl "e qq{${q}Perl$q}, \"$Q@A$Q\" " one-liner
Perl one-liner
PowerShell is weirder. (Did I mention you'd be better off with a real Shell?) You must use outer single quotes, but you still need to protect double quotes:
pl 'echo qq{${quote}Perl$quote}, \"$Quote@ARGV$Quote\" ' one-liner
pl 'e qq{${q}Perl$q}, \"$Q@A$Q\" ' one-liner
Perl one-liner
While the old Windows 10 terminal understands ANSI escape sequences, it makes it horribly hard to activate them. Therefore, they're off by default, requiring --color to override that choice.