App-repeat

 view release on metacpan or  search on metacpan

script/4x  view on Meta::CPAN

#!perl

use 5.010001;
use strict;
use warnings;
use Log::ger;

use Getopt::Long::Less;

our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
our $DATE = '2024-12-10'; # DATE
our $DIST = 'App-repeat'; # DIST
our $VERSION = '0.006'; # VERSION

our %Opts = (
    n => undef,
    until_time => undef,
    max => undef,

    delay => undef,
    delay_max => undef,
    delay_strategy => undef,

    until_fail => 0,
    until_success => 0,
    dry_run => 0,
    dry_run_delay => 0,
);

my $Exit_Code = 0;

sub parse_cmdline {
    my $res = GetOptions(
        'n=i'              => \$Opts{n},
        'until-time=s'     => sub {
            require DateTime::Format::Natural;
            my $dt = DateTime::Format::Natural->new->parse_datetime($_[1]);
            warn "TZ is not set!" unless $ENV{TZ};
            $dt->set_time_zone($ENV{TZ});
            log_debug "--until-time is set to %s (%.3f)", "$dt", $dt->epoch;
            $Opts{until_time} = $dt->epoch;
        },
        'max=i'            => \$Opts{max},

        'delay|d=f'        => \$Opts{delay},
        'delay-min=f'      => \$Opts{delay_min},
        'delay-max=f'      => \$Opts{delay_max},
        'delay-strategy=s' => sub {
            no strict 'refs';  ## no critic: TestingAndDebugging::ProhibitNoStrict
            my $s = $_[1];
            my ($mod, $args) = $s =~ /\A(\w+(?:\::\w+)*)(?:[=,](.*))?/;
            $args //= ''; $args = { split /,/, $args };
            $mod = "Algorithm::Backoff::$mod";
            (my $modpm = "$mod.pm") =~ s!::!/!g;
            require $modpm;
            $s = $mod->new(%$args);

            $Opts{delay_strategy} = $s;
        },

        'until-fail'       => \$Opts{until_fail},
        'until-success'    => \$Opts{until_success},

        'dry-run!'         => \$Opts{dry_run},
        'dry-run-delay'    => \$Opts{dry_run_delay},
        'help|h|?'         => sub {
            print <<USAGE;
Usage:
  repeat [repeat options] -- [command] [command options ...]
  repeat --help, -h, -?
Options:
  -n=i
  --until-time=s
  --max=i
  --until-fail
  --until-success
  --delay=f, -d
  --delay-strategy=s
  --(no-)dry-run
  --dry-run-sleep
For more details, see the manpage/documentation.
USAGE
            exit 0;
        },
    );
    exit 99 if !$res;

    if ($0 =~ m![/\\](\d+)(?:times|x)$!) {
        $Opts{n} //= $1;
    } elsif ($0 =~ m![/\\](twice)$!) {
        $Opts{n} //= 2;
    }

    unless ((defined $Opts{n}) xor (defined $Opts{until_time})) {
        warn "repeat: Please specify -n or --until-time\n";
        exit 99;
    }

script/4x  view on Meta::CPAN


This will run C<somecmd> 10 times:

 % repeat -n10 -- somecmd --cmdopt

This will run C<somecmd> 10 times with a delay of 2 seconds in between:

 % repeat -n10 -d2 -- somecmd --cmdopt

This will repeatedly run C<somecmd> until tomorrow at 10am with a delay of
between 2 and 10 seconds in between (keywords: jitter):

 % repeat --until-time 'tomorrow at 10AM' --delay-min=2 --delay-max=10 -- somecmd --cmdopt

This will run C<somecmd> 10 times with exponentially increasing delay from 1, 2,
4, and so on:

 % repeat -n10 --delay-strategy=Exponential=initial_delay,1 -- somecmd --cmdopt

Dry-run mode and show debugging messages: do not actually run the command, just
show it in the log (require L<Log::ger::Screen>).

 % TRACE=1 PERL5OPT=-MLog::ger::Screen repeat -n10 --dry-run -- foo

=head1 DESCRIPTION

C<repeat> is a CLI utility that lets you repeat running a command a number of
times. In its most basic usage, it simplifies this shell construct:

 % for i in `seq 1 10`; do somecmd --cmdopt; done

into:

 % repeat -n10 -- somecmd --cmdopt

You can either specify a fixed number of times to repeat (C<-n>) or until a
certain point of time (C<--until-time>) with an optional maximum number of
repetition (C<--max>).

Delay between command can be specified as a fixed number of seconds (C<--delay>)
or a random range of seconds (C<--delay-min>, C<--delay-max>) or using a backoff
strategy (see L<Algorithm::Backoff>).

You can opt to bail immediately after a failure (C<--until-fail>) or after a
success (C<--until-success>).

=head1 OPTIONS

=over

=item * --help, -h, -?

=item * -n

Uint. Number of times to run the command. Alternatively, you can use
C<--until-time> (with optional C<--max>) instead.

=item * --until-time

String representation of time. Will be parsed using
L<DateTime::Format::Natural>. Alternatively, you can use C<-n> instead.

Note that dependency to L<DateTime::Format::Natural> is declared optionally
(RuntimeSuggests). You need to install the module first if you want to use
C<--until-time>.

=item * --max

Uint. When C<--until-time> is specified, specify maximum number of repetition.

=item * --delay, -d

Float. Number of seconds to delay between running a command. Alternatively, you
can specify C<--delay-min> and C<--delay-max>, or C<--delay-strategy>.

=item * --delay-min

Float.

=item * --delay-max

Float.

=item * --delay-strategy

Str. Pick a backoff strategy from L<Algorithm::Backoff>. It should be a module
name under C<Algorithm::Backoff::> namespace, without the prefix, optionally
followed by C<,> (or C<=>) and a comma-separated list of arguments. For example:

 Exponential=initial_delay,1,max_delay,10

Note that the failure delay values are used as the delays.

Also note that the dependency is not specified explicitly; you have to install
it yourself.

=item * --until-fail

Bool. Stop repeating as soon as exit code of command is non-zero (failure).
Alternatively, you can specify C<--until-success> instead.

=item * --dry-run

Dry-run mode. Don't actually run the command, just show it in the log. But
delays will be performed; use C<--dry-run-delay> to skip delaying also.

=item * --dry-run-delay

Dry-run mode for delay. Don't actually delay, just show it in the log.

=back

=head1 ENVIRONMENT

=head1 HOMEPAGE

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

=head1 SOURCE

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

=head1 SEE ALSO



( run in 1.064 second using v1.01-cache-2.11-cpan-e1769b4cff6 )