http://qs1969.pair.com?node_id=44963

zzspectrez has asked for the wisdom of the Perl Monks concerning the following question:

Good evening fellow monks!!

I have this code I have been using that copies files between windows computers on a local network. Some of the files are quite large (100+megs) and can take up to 2 minutes to transfer. This is no problem, but sometimes leaves you wondering if your program is still running or has it crashed. So I was wondering how I could implement some kind of copy progress status.

The following code is what I came up with. What do the fellow monks think? Any suggestions? Improvements?

#!/usr/bin/perl -w use strict; sub CopyFileProgress ( ) { my $src = shift; my $dst = shift; my $callback = shift; my $num_read; my $num_wrote; my $buffer; my $perc_done = 0; open (SRC, "< $src") or die "Could not open source file [$src]: $! +\n"; open (DST, "> $dst") or die "Could not open destination file [$dst +]: $!\n"; binmode SRC; binmode DST; my $filesize = (-s $src) or die "File has zero size.\n"; my $blksize = int ($filesize / 10); while (1) { $num_read = sysread(SRC, $buffer, $blksize); last if ($num_read == 0); + die ("Error reading from file [$src]: $!\n") if (!defined($num +_read)); my $offset = 0; while ($num_read){ $num_wrote = syswrite(DST,$buffer,$num_read,$offset); die ("Error writing to file [$dst]: $!\n") if (!defined($n +um_wrote)); $num_read -= $num_wrote; $offset += $num_wrote; } $perc_done += 10 unless $perc_done == 100; &$callback($perc_done) or die ("Copy canceled.\n"); } } sub FileProgress () { my $percent = shift; print "$percent% done\n"; return 1; } if (@ARGV == 2) { my $source = shift @ARGV; my $destination = shift @ARGV; &CopyFileProgress($source,$destination,\&FileProgress); }else{ print "\n\nUSAGE: copy.pl <source filename> <destination filename> +\n\n"; }

Later....
zzSPECTREz

Replies are listed 'Best First'.
Re: File copy progress.
by repson (Chaplain) on Dec 05, 2000 at 10:52 UTC
    Your could look at using a 'whirlygig'.
    Simply fork and do the copy in the child while the parent shows that it is still running. When the child ends it will send a SIGCHLD. Or do the copy in the parent and kill the child when you finish.
    Or in the parent you could periodically check the size of the destination file (though I think windows keeps it at 0 until the file is done) and display that somehow.
Re: File copy progress.
by Fastolfe (Vicar) on Dec 05, 2000 at 18:44 UTC
    I'm not sure if this will work under Windows, but under Unix you can use \r instead of \n to return the cursor to the beginning of the line again. This makes percentage status reports "nicer" by keeping them on the same line. You just see the number go up. When you're done, print a \n.

      Actually, I tested this under windows. At first I didnt use binmode on the filehandles, and it wouldnt work with binary files. But once I added that it worked fine.


      Thanks for the sugestion of using \r, Ill try that.


      zzSPECTREz


      UPDATE:In rereading your post, I think I missuderstood it.. Ill have to try the \r to see if it works under windows.

(Guildenstern) Re: File copy progress.
by Guildenstern (Deacon) on Dec 05, 2000 at 19:35 UTC
    You can always use Win32::FileOp. If you use CopyConfirm or CopyConfirmEach, you get the same Windows dialog that shows files being copied as when you use Explorer.

    Guildenstern
    Negaterd character class uber alles!

      Actually, before writing this bit of code I was looking into that module. I downloaded it but cant get it to work yet. Cant use the module because it says I dont have the module win32::Api which it depends on. Tried ppm the module and it says it allready exists! Fun.. Ehh.

      So impatient, I decided to see if I could figure something out myself. Plus this works on my linux machines too.

      Thanks..
      zzSPECTREz

Re: File copy progress.
by zigster (Hermit) on Dec 05, 2000 at 14:41 UTC
    The code prints out that it has done 100% twice ;-) this is probs bad.. Hows about changing it to show the bytes read / bytes left to read. This would also allow you to change the block size for larger files. The simplest way to do this would be to incr a variable after the sysread and print that rather than the percentage. I could not see why it was printing 100% twice but I did not look hard.

    On a stylistic point, why did you use a sub to print out the message, and why did you pass that sub as an arg? Just interested to hear your rational.

    Nice little bit of code, ++, I have added it to my personal repository of bits of useful stuff .. nice one. --

    Zigster

      Yeah.. I know it prints 100% twice. It only does that if the file doesnt evenly divide by 10. This code was a quick hack of 15 minutes to see if the idea would work. I still need to mess with it to make it work better.

      The reason why I pass a subroutine as a variable to the copy routine is just so I could have the calling program determine how to display the status. That way I could just redefine that subroutine and have it display an actually prograss bar or something..

      zzSPECTREz

        Sweet, a progress bar would be a nice touch.
        --

        Zigster
Re: File copy progress.
by OzzyOsbourne (Chaplain) on Dec 06, 2000 at 18:09 UTC

    For some file find scripts, I have the script print a . every time it loops. It's not very elegant, but it's easy to tell that the script is running.

    -OzzyOsbourne