App-pl
view release on metacpan or search on metacpan
pod/examples.pod view on Meta::CPAN
pl -Pi '/\S/' file*
=item Move a Line Further Down in Each File
Assume we have lines matching "from" followed by lines matching "to". The
former shall move after the latter. This loops over each file, replacing it
with the modified output. The flip-flop operator becomes true when matching
the 1st regexp. Capture something in there to easily recognize it's the
first, keep the line in a variable and empty C<$_>. When C<$1> is again true,
it must be the last matching line. Append the keep variable to it.
pl -pi 'if( /(f)rom/.../(t)o/ ) {
if( $1 eq "f" ) { $k = $_; $_ = "" } elsif( $1 ) { $_ .= $k }
}' file*
=item Rename a File Depending on Contents
This reads each file in an B<-n> loop. When it finds the C<package>
declaration, which gives the logical name of this file, it replaces
double-colons with slashes. It renames the file to the result. The C<last>
statement then makes this the last line read of the current file, continuing
with the next file:
pl -n 'if( s/^\s*package\s+([^\s;]+).*/$1/s ) {
s!::!/!g;
rename $ARGV, "$_.pm" or warn "$ARGV -> $_.pm: $!\n";
last;
}' *.pm
pl -n 'if( s/^\s*package\s+([^\s;]+).*/$1/s ) {
s!::!/!g;
rename $A, "$_.pm" or warn "$A -> $_.pm: $!\n";
last;
}' *.pm
This assumes all files are at the root of the destination directories. If not
you must add the common part of the target directories before C<$_>.
On Windows this won't quite work, because that locks the file while reading.
So there you must add C<close ARGV;> (or C<close A;>) before the C<rename>.
For Java, it's a bit more complicated, because the full name is split into a
C<package> followed by a C<class> or similar statement. Join them when we
find the latter:
pl -n 'if( /^\s*package\s+([^\s;]+)/ ) {
$d = $1 =~ tr+.+/+r;
} elsif( /^\s*(?:(?:public|private|protected|abstract|sealed|final)\s+)*(?:class|interface|enum|record)\s+([^\s;]+)/ ) {
rename $ARGV, "$d/$1.java" or warn "$ARGV -> $d/$1.java: $!\n";
last;
}' *.java
pl -n 'if( /^\s*package\s+([^\s;]+)/ ) {
$d = $1 =~ tr+.+/+r;
} elsif( /^\s*(?:(?:public|private|protected|abstract|sealed|final)\s+)*(?:class|interface|enum|record)\s+([^\s;]+)/ ) {
rename $A, "$d/$1.java" or warn "$A -> $d/$1.java: $!\n";
last;
}' *.java
=item Delete Matching Files, Except Last One
If you have many files, which sort chronologically by name, and you want to
keep only the last one, it can be quite painful to formulate Shell patterns.
So check on each iteration of the B<-o> loop, if the index C<$ARGIND> (or
C<$I>) is less than the last, before unlinking (deleting). If you want to test
it first, replace C<unlink> with C<e(cho)>:
pl -o 'unlink if $ARGIND < $#ARGV' file*
pl -o 'unlink if $I < $#A' file*
If your resulting list is too long for the Shell, let Perl do it. Beware that
the Shell has a clever ordering of files, while Perl does it purely lexically!
In the B<-A> code the result is assigned to C<@A(RGV)>, as though it had come
from the command line. This list is then popped (shortened) in B<-B> begin
code, instead of checking each time. Since the programs don't contain special
characters, you don't even need to quote them:
pl -oA '<file*>' -B pop unlink
You can exclude files by any other criterion as well:
pl -oA 'grep !/keep-me/, <file*>' unlink
=back
=head2 File Statistics
I<42% of statistics are made up! :-)>
=over
=item Count Files per Suffix
Find and pl both use the B<-0> option to allow funny filenames, including
newlines. Sum up encountered suffixes in sort-numerically-at-end hash
C<%N(UMBER)>:
find -type f -print0 |
pl -0ln 'm@[^/.](\.[^/.]*)?$@;
++$NUMBER{$1 // "none"}'
find -type f -print0 |
pl -0ln 'm@[^/.](\.[^/.]*)?$@;
++$N{$1 // "none"}'
> 4: .3
> 4: .SHs
> 4: .act
> ...
> 88: .json
> 108: .tml
> 136: .xml
> 224: .yml
> 332: .xs
> 376: .sh
> 412: .ucm
> 444: .PL
> 512: .c
> 640: .h
> 696: .txt
> 950: .pod
> 1392: .pl
> 2988: none
> 3264: .pm
( run in 0.615 second using v1.01-cache-2.11-cpan-2398b32b56e )