in reply to Progress Bar in Perl script

You could take a look at Smart::Comments - this might do what you want (it doesn't require a MAX counter). Here's a quick commandline test you can run:

$ perl -Mstrict -Mwarnings -e ' use Smart::Comments; for (1 .. 500) { ### Running... done qx{ls -l > /dev/null 2>&1}; } '

Here's my take on a Roll-Your-Own spinner. This has 6 tests: the first 3 are expected to fail; the next three have fast, medium and slow spin rates. The spinner becomes an asterisk if the process completes successfully.

#!/usr/bin/env perl use strict; use warnings; my $CMD = q{ls -l > /dev/null}; for ([], [q{}], [q{dummy}], [$CMD, 10], [$CMD], [$CMD, 50]) { eval { run_with_progress(@$_); }; warn $@ if $@; } sub run_with_progress { print q{-} x 60, qq{\nStarting ...\n}; my ($cmd, $skip) = @_; die q{Nothing to run!} unless $cmd; $skip ||= 25; # Arbitrary my @sails = map { "\b$_" } qw{- \ | /}; my $sail = 0; { local $| = 1; print q{Running: }; for (0 .. 1000) { system $cmd and die qq{'$cmd' failed! $? : $!}; print $sails[$sail++ % @sails] if not $_ % $skip; } } print qq{\b*\nFinished.\n}; }

Output:

$ pm_text_spinner_ryo.pl ------------------------------------------------------------ Starting ... Nothing to run! at ./pm_text_spinner_ryo.pl line 20. ------------------------------------------------------------ Starting ... Nothing to run! at ./pm_text_spinner_ryo.pl line 20. ------------------------------------------------------------ Starting ... Running: Can't exec "dummy": No such file or directory at ./pm_text_s +pinner_ryo.pl line 32. 'dummy' failed! -1 : No such file or directory at ./pm_text_spinner_ry +o.pl line 32. ------------------------------------------------------------ Starting ... Running: * Finished. ------------------------------------------------------------ Starting ... Running: * Finished. ------------------------------------------------------------ Starting ... Running: * Finished.

Update: I revisited this and reworked run_with_progress() to use Smart::Comments but with a spinner instead of the normal progress bar. The messages are the same as shown above and the spin rates vary in the same manner; the formatting is different.

sub run_with_progress { use Smart::Comments; ### Starting ... my ($cmd, $skip) = @_; die q{Nothing to run!} unless $cmd; $skip ||= 25; # Arbitrary my @sails = map { ($_) x $skip } qw{- \ | /}; my $sail = 0; for (0 .. 1000) { ### Running: $sails[$sail++ % @sails] system $cmd and die qq{'$cmd' failed! $? : $!}; } ### Finished. no Smart::Comments; return; }

This code is a lot cleaner; however, I'm not a big fan of the output.

$ pm_text_spinner_sc_spin.pl ### Starting ... Nothing to run! at ./pm_text_spinner_sc_spin.pl line 22. ### Starting ... Nothing to run! at ./pm_text_spinner_sc_spin.pl line 22. ### Starting ... Running: - Can't exec "dummy": No such file or + directory at ./pm_text_spinner_sc_spin.pl line 29. 'dummy' failed! -1 : No such file or directory at ./pm_text_spinner_sc +_spin.pl line 29. ### Starting ... ### Finished. ### Starting ... ### Finished. ### Starting ... ### Finished.

Update 2: I also reworked run_with_progress() to use Term::Spinner. Messages, spin rates, etc. work the same as the other two; the output is almost identical to the Roll-Your-Own version. This would be my favourite ... at least until I find something better. :-)

sub run_with_progress { print q{-} x 60, qq{\nStarting ...\n}; my ($cmd, $skip) = @_; die q{Nothing to run!} unless $cmd; $skip ||= 25; # Arbitrary use Term::Spinner; my $spinner = Term::Spinner::->new(); print q{Running: }; for (0 .. 1000) { system $cmd and die qq{'$cmd' failed! $? : $!}; $spinner->advance() if not $_ % $skip; } $spinner->finish(); print qq{\nFinished.\n}; undef $spinner; return; }

-- Ken

Replies are listed 'Best First'.
Re^2: Progress Bar in Perl script
by slayedbylucifer (Scribe) on Jul 09, 2012 at 09:30 UTC
    This is a great explanation. thank you for your time. i will work on the 3rd option that you mentioned.
Re^2: Progress Bar in Perl script
by regexes (Hermit) on Mar 13, 2013 at 09:40 UTC
    I've been away from Perl for too long... I hope I'm not misunderstanding some fundamental principal here...

    When I run this code from the command line, what I don't understand is how the "spinner" is printing over itself to the screen.
    I understand that @sail is being interated through by incrementing $sail, but what part of this code is making the contents of @sail print on top of each other, i.e. making the "spinner" effect?

    my @sails = map { "\b$_" } qw{- \ | /}; my $sail = 0; { local $| = 1; print q{Running: }; for (0 .. 1000) { system $cmd and die qq{'$cmd' failed! $? : $!}; print $sails[$sail++ % @sails] if not $_ % $skip; } }


    Any help here? What am I overlooking?

      Each of the sails is created by prepending a "\b" to the spinner character:

      my @sails = map { "\b$_" } qw{- \ | /};

      "\b" is a backspace character; so, when each sail is printed, it deletes the previous one. In the initial state, "Running:" is followed by two spaces (print q{Running:  };); the second space is overwritten by the first sail.

      "\b", and other special characters, are listed in perlop: Quote and Quote-like Operators.

      -- Ken

        D'oh! Thanks! I knew I was overlooking something...