Printing right arrow vs. a sane progressbar library

I want to track the progress of a set of (forkoff’d) processes in
a simple way. What I have in mind is a line of dots equal in length
to the number of processes, and once a process number X finishes,
it prints \r, then advances X characters to the right and prints o
– so after three of eight processes finish, it could look like …o…oo.

The closest I got is the below (in one line, so easily pastable to IRB):

print ‘.’ * 25; (0…25).sort_by{rand}.each { |n| print “\r”; print
“\c[[C” * n; print ‘o’; sleep 0.5 }; print “\n”

It seems to work in IRB and in a script (after $stdout.sync = true),
unless the number of processes is larger than the terminal window’s
width.

Is this a sane method of doing what I want to do, or does
it just ‘happens to work’ in GNOME Terminal on my Ubuntu?

Is there a saner way to advance by a char than printing the right arrow?

On a similar note: Is there a simple progressbar library for Ruby
(ideally if itcan be somehow used with forkoff’d processes, but I’m
aware it’d require some kind of passing of state to the outside of the
block)?

Is there anything else than Ruby/ProgressBar¹ (say, in a gem…)?

¹ Ruby/ProgressBar: A Text Progress Bar Library for Ruby

(I’m aware the above is prone to a race condition if two processes
finish at the same time and the second one of them prints its \r
before the first one prints its o, but let’s disregard this for now.)

– Shot

On Nov 22, 2008, at 8:18 PM, Shot (Piotr S.) wrote:

print ‘.’ * 25; (0…25).sort_by{rand}.each { |n| print “\r”; print
arrow?
status=‘.’*25; print status; (0…25).sort_by{rand}.each{|n|
status[n]=‘o’; print “\r#{status}”; sleep(0.1+rand/10.0)};puts

While it still has trouble with more values that screen columns, it
should be a bit better with race conditions. You could, perhaps, add
a clear-to-EOL sequence, too.

-Rob

(I’m aware the above is prone to a race condition if two processes
finish at the same time and the second one of them prints its \r
before the first one prints its o, but let’s disregard this for now.)

– Shot

If I was to use Bugzilla when I was a tester, I’d quit
and become a gardener. – a Syllable OS developer

Rob B. http://agileconsultingllc.com
[email protected]

Rob B.:

On Nov 22, 2008, at 8:18 PM, Shot (Piotr S.) wrote:

I want to track the progress of a set of
(forkoff’d) processes in a simple way. […]

print ‘.’ * 25; (0…25).sort_by{rand}.each { |n| print “\r”; print “\c[[C” * n; print ‘o’; sleep 0.5 }; print “\n”

status=’.’*25; print status; (0…25).sort_by{rand}.each{|n| status[n]=‘o’;
print “\r#{status}”; sleep(0.1+rand/10.0)};puts

A very good idea, but unfortunately it doesn’t work in a forkoff’d
block; every block gets its own copy of the original status and so
generates a line with 24 dots and one o:

shot@devielle:~$ irb -rubygems

require ‘forkoff’
=> true

status=’.’*25; print status; (0…25).sort_by{rand}.forkoff{|n| status[n]=‘o’; print “\r#{status}”; sleep(0.1+rand/10.0)};puts
…o…
=> nil

Hence my need for a right arrow, or for printing a ‘transparent’
character that doesn’t overwrite what’s already under the cursor.

You could, perhaps, add a clear-to-EOL sequence, too.

I’m sorry, but I’m a bit braindead today; where would I want to use
this? (And, when we’re at it, what’s the escape sentence for it?)

Thanks a lot for your interest in this thread and your reply!

– Shot

On Nov 23, 2008, at 12:30 PM, Shot (Piotr S.) wrote:

status[n]=‘o’;

status[n]=‘o’; print “\r#{status}”; sleep(0.1+rand/10.0)};puts
…o…
=> nil

Hence my need for a right arrow, or for printing a ‘transparent’
character that doesn’t overwrite what’s already under the cursor.

I took a quick look at the code for forkoff and unless you’re
increasing the processes (or just looking for one that is “slow”),
this doesn’t seem to help much.

You could, perhaps, add a clear-to-EOL sequence, too.

I’m sorry, but I’m a bit braindead today; where would I want to use
this? (And, when we’re at it, what’s the escape sentence for it?)

That sequence and other ANSI control sequences can be found via the
web, for example, at:
ANSI escape code - Wikipedia

Thanks a lot for your interest in this thread and your reply!

– Shot

A distributed system is one in which I cannot get something done
because
a machine I’ve never heard of is down. – Leslie
Lamport

Perhaps a sequence of:
“[[s[[#{n+1}Go[[u”
if your terminal supports the save/restore cursor position

print ‘.’ * 25; (0…25).sort_by{rand}.forkoff { |n| sleep(0.25+rand/
10.0); print “\033[s\033[#{n+1}Go\033[u” }; print “\n”

Or manually go back to the “end” of the line each time:
“\033[#{n+1}Go\033[#{25+1}G”

print ‘.’ * 25; (0…25).sort_by{rand}.each { |n| sleep(0.2+rand/2.0);
print “\033[#{n+1}Go\033[26G” }; print “\n”

Perhaps get the status via some other mechanism in another process
(but that’s going to fly away from a “simple way” even faster).

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

Rob B.:

On Nov 23, 2008, at 12:30 PM, Shot (Piotr S.) wrote:

A very good idea, but unfortunately it doesn’t work in a forkoff’d
block; every block gets its own copy of the original status and so
generates a line with 24 dots and one o […]

Hence my need for a right arrow, or for printing a ‘transparent’
character that doesn’t overwrite what’s already under the cursor.

I took a quick look at the code for forkoff and unless you’re
increasing the processes (or just looking for one that is “slow”),
this doesn’t seem to help much.

Erm, I’m not sure what doesn’t seem to help much. :slight_smile: Using the right
arrow trick works as expected (as opposed to editing a string that’s
unfortunately not the same object across the processes):

shot@devielle:~$ irb -rubygems

require ‘forkoff’
=> true
print ‘.’ * 25; (0…25).sort_by{rand}.forkoff { |n| print “\r”; print “\c[[C” * n; print ‘o’; sleep 0.5 }; print “\n”
ooooooooooooooooooooooooo
=> nil

(Although it’s more prone to race conditions than your solutions below.)

In my actual case, I want a general mechanism that runs parallel
processes in some way (forkoff for now, maybe DRb in the future)
and report back some kind of a visual information on how much is
being done over time.

As I assume each of such parallel runs won’t be able to access the
actual objects outside the forked-off block (only copies of them),
I believe I can’t employ a simple, tick-based progressbar; the above
was my idea to side-step the need of communication with the parent
(although I could perhaps make the code that collects¹ the returned
values trigger/tick the progressbar as soon as a result comes in…).

¹ forkoff has this nice feature that it handles the collecting of the
returned values transparently, so I simply don’t have any code that
collects the results at the moment…

That sequence and other ANSI control sequences can be found via the
web, for example, at: ANSI escape code - Wikipedia

Ah, thank you! I started this quest by recalling how \b and \r work,
which led me to the ASCII control characters; I totally forgot to
look at ANSI stuff, just recalled I can come up with the sequence
for the right arrow by cating stdin to a file and then looking
at it. I should’ve thought ‘ANSI!’ when I saw ^[[C. :slight_smile:

Perhaps a sequence of:
“[[s[[#{n+1}Go[[u”
if your terminal supports the save/restore cursor position

Or manually go back to the “end” of the line each time:
“\033[#{n+1}Go\033[#{25+1}G”

Actually, a simple “\033[#{n+1}Go” works well enough; thanks! The trick
with going to the end of the line each time makes it easier on the eyes
if the cursor is visible, so I’ll most probably end up using it. Thanks
again!

(And just before sending this email I found out that “\033[?25l” makes
the cursor disappear, and so seems to be the cleanest solution without
any output-related race conditions whatsoever.) :slight_smile:

– Shot