#!perl

use strict;
use warnings;
use App::perlmv;
use Getopt::Long::Complete; # just so we can detect script as using Getopt::Long::Complete

our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
our $DATE = '2023-11-17'; # DATE
our $DIST = 'App-perlmv'; # DIST
our $VERSION = '0.609'; # VERSION

my $pmv = App::perlmv->new;
#$pmv->{ 'mode' } = 'move'; # the default
$pmv->run;

1;
# ABSTRACT: Rename/move files using Perl code
# PODNAME: perlmv

__END__

=pod

=encoding UTF-8

=head1 NAME

perlmv - Rename/move files using Perl code

=head1 VERSION

This document describes version 0.609 of perlmv (from Perl distribution App-perlmv), released on 2023-11-17.

=head1 SYNOPSIS

Usage:

 # Show help
 perlmv -h

 # Execute a single scriptlet
 perlmv [options] <scriptlet> <file...>

 # Execute code from command line
 perlmv [options] -e <code> <file...>

 # Execute multiple scriptlets/command-line codes
 perlmv [options] [ -x <scriptlet> | -e <code> ]+ <file...>

 # Create a new scriptlet
 perlmv -e <code> -w <name>

 # List available scriptlets
 perlmv -l

 # Show source code of a scriptlet
 perlmv -s <name>

 # Delete scriptlet
 perlmv -d <name>

=head2 Usage examples

 $ ls -1
 A.txt
 B1
 c2.txt
 D3.pl
 D4.pl

Rename files with prewritten scriptlet (B<remove-common-prefix>) and show
(B<-v>) each file as it is being renamed.

 $ perlmv -v remove-common-prefix file1.jpg file2.zip files.txt
 `file1.jpg` -> `1.jpg`
 `file2.zip` -> `2.zip`
 `files.txt` -> `s.txt`

Specify script in command line (B<-e>) but do not actually move files (B<-d>,
dry-run mode):

 $ perlmv -de 's/\d+//g' *
 DRYRUN: `B1` -> `B`
 DRYRUN: `c2.txt` -> `c.txt`
 DRYRUN: `D3.pl` -> `D.pl`
 DRYRUN: `D4.pl` -> `D.pl.1`

Really rename the files this time:

 $ perlmv -e 's/\d+//g' *

Execute multiple scriptlets and/or command-line code:

 $ ls -1
 a.txt
 b.html
 c.ini

 $ perlmv -vx to-number-ext -e '"file$_"' *
 `a.txt` -> `1.txt` -> `file1.txt`
 `b.html` -> `2.html` -> `file2.html`
 `c.ini` -> `3.ini` -> `file3.ini`

Save Perl code as scriptlet (in ~/.perlmv/scriptlets/):

 $ perlmv -e 's/\d+//g' -w remove-digits

List all scriptlets (add B<-v> to also show their contents):

 $ perlmv -l
 lc
 uc
 remove-digits

Show (B<-s>) source code of scriptlet:

 $ perlmv -s remove-digits
 s/\d+//g

Remove (B<-D>) scriptlet:

 $ perlmv -D remove-digits

=head2 More examples

From my real-world usage.

Rename .flv partial files from my Firefox browser cache into ordered names
(part01.flv, part02.flv, and so on):

 $ ls --sort=t -r *01 | xargs perlmv -Tdx to-number-ext -e '"part$_.flv"'
 DRYRUN: `15D9F85Ad01` -> `01` -> `part01.flv`
 DRYRUN: `FF8EB240d01` -> `02` -> `part02.flv`
 DRYRUN: `9031E9A8d01` -> `03` -> `part03.flv`
 ...

=head1 DESCRIPTION

Perlmv lets you rename/move files using Perl code. All the Perl code needs to do
is modify the filename in C<$_> (or just return a value) and perlmv will do the
rest (actual renaming, recursive renaming, handling filename conflicts, dry-run
mode, etc.).

New filename will be taken from the new value of $_ (if it is modified) or the
last expression. So '$_ = lc' and 'lc' are equivalent.

The following variables are also available to the Perl code:

=over 4

=item * C<$PERLMV> (object)

The L<App::perlmv> object. No matter what CLI is being run (L<perlmv>,
L<perlrename>, L<perlcp>, ...) the variable is still L<$PERLMV>. Can be used to
get various settings. The Perl code is not supposed to modify this value.

=item * C<$FILES> (arrayref)

The list of items to be processed. The Perl code is not supposed to modify this
value.

=item * C<$FILE> (string)

The filename of item (without path). The Perl code is not supposed to modify
this value.

=item * C<$FILENUM> (int)

The index of $FILES currently being processed (starting from 0). The Perl code
is not supposed to modify this value.

=item * C<$DIR> (string)

The directory of item (absolute path). The Perl code is not supposed to modify
this value.

=item * C<$PARENT> (string)

The name of parent (containing) directory of the item (without path). The Perl
code is not supposed to modify this value.

=item * C<$ARGS> (hashref)

This are arguments specified by user via the C<-a> (C<--arg> option), e.g. C<<
-a foo=1 -a bar=2 >> will cause this variable to have the value of C<< {foo=>1,
bar=>2} >>.

The Perl code is not supposed to modify this value.

=back

Perl code will first be run (eval-ed) once at the beginning for testing, with
C<-TEST> as the filename in C<$_> (and C<$TESTING> will be set to true). Perl
code is not run under strict/warnings. Perl code is run under
C<App::perlmv::code> namespace.

When using the L<App::perlmv> library, there is also the the so-called cleaning
phase (in which Perl code will be run with $CLEANING set to true) that can be
used for cleaning purposes in between runs. This is however irrelevant to
B<perlmv> command as there is only a single run.

Perl code can be specified directly from the command line (using B<-e>), or (in
order of precedence from the highest) by name in C<~/.perlmv/scriptlets/NAME>,
or in C</usr/share/perlmv/scriptlets/>, or in C<%scriptlets> in
L<App::perlmv::scriptlets>, or in C<%scriptlets> in
L<App::perlmv::scriptlets::std>, or in C<$SCRIPTLET> in
L<App::perlmv::scriptlet::*> (for examples see
L<App::perlmv::scriptlet::rename_common_prefix> and
L<App::perlmv::scriptlet::rename_common_suffix>).

=head1 BUGS/TODOS

=over

=item * Patches for Windows welcome.

=item * Scriptlet should be able to receive arguments.

=back

=head1 OPTIONS

 -c  (--compile) Only test compile code, do not run it on the arguments
 -D <NAME> (--delete) Delete scriptlet
 -d  (--dry-run) Dry-run (implies -v)
 -e <CODE> (--execute) Specify Perl code to rename file (\$_). Can be specified
     multiple times.
 -f  (--files) Only process files, do not process directories
 -h  (--help) Show this help
 -l  (--list) list all scriptlets
 -M <MODE> (--mode) Specify mode, default is 'rename' (or 'r'). Use 'copy' or
     'c' to copy instead of rename, 'symlink' or 's' to create a symbolic link,
     and 'link' or 'l' to create a (hard) link.
 -N  (--no-dry-run) Turn off dry-run mode.
 -o  (--overwrite) Overwrite (by default, ".1", ".2", and so on will be appended
     to avoid overwriting existing files)
 -p  (--parents) Create intermediate directories
 -R  (--recursive) Recursive
 -r  (--reverse) reverse order of processing (by default asciibetically)
 -S  (--no-symlinks) Do not process symlinks
 -s <NAME> (--show) Show source code for scriptlet
 -T  (--no-sort) do not sort files (default is sort ascibetically)
 -V  (--version) Print version and exit
 -v  (--verbose) Verbose
 -w <NAME> (--write) Write code specified in -e as scriptlet
 -x <NAME> Execute a scriptlet. Can be specified multiple times. -x is optional
     if there is only one scriptlet to execute, and scriptlet name is specified
     as the first argument, and there is no -e specified.
 -a <arg=value> (--arg) Supply arguments for code/scriptlet.

Details on some options:

=over

=item * -N, --no-dry-run

This turns off the dry-run mode. Useful if you have a shell alias or wrapper
that uses C<-d> to turn on dry-run mode by default (for safety) and you want to
override by specifying C<-N> after.

=back

=head1 COMPLETION

This script has shell tab completion capability with support for several
shells.

=head2 bash

To activate bash completion for this script, put:

 complete -C perlmv perlmv

in your bash startup (e.g. C<~/.bashrc>). Your next shell session will then
recognize tab completion for the command. Or, you can also directly execute the
line above in your shell to activate immediately.

It is recommended, however, that you install modules using L<cpanm-shcompgen>
which can activate shell completion for scripts immediately.

=head2 tcsh

To activate tcsh completion for this script, put:

 complete perlmv 'p/*/`perlmv`/'

in your tcsh startup (e.g. C<~/.tcshrc>). Your next shell session will then
recognize tab completion for the command. Or, you can also directly execute the
line above in your shell to activate immediately.

It is also recommended to install C<shcompgen> (see above).

=head2 other shells

For fish and zsh, install C<shcompgen> as described above.

=head1 FAQ

=head2 How is perlmv different from other similar solutions?

Compared to L<rename> from L<File::Rename>, perlmv offers scriptlets, recursive
mode, automatic renaming in case of conflicts. Instead of rename, you can also
link, symlink, or copy.

L<pmv> from L<File::PerlMove> also allows linking/symlinking instead of rename,
but it does not have recursive mode or scriptlets.

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/App-perlmv>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-App-perlmv>.

=head1 SEE ALSO

L<rename> from (L<File::Rename>

L<pmv> from L<File::PerlMove>

Other binaries in this distribution: L<perlrename>, L<perlcp>, L<perlln>,
L<perlln_s>.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 CONTRIBUTING


To contribute, you can send patches by email/via RT, or send pull requests on
GitHub.

Most of the time, you don't need to build the distribution yourself. You can
simply modify the code, then test via:

 % prove -l

If you want to build the distribution (e.g. to try to install it locally on your
system), you can install L<Dist::Zilla>,
L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
that are considered a bug and can be reported to me.

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2023, 2022, 2020, 2015, 2014, 2013, 2012, 2011, 2010 by perlancar <perlancar@cpan.org>.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-perlmv>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=cut