Showing progress when running the system () command in Perl

I have a Perl script that performs some tasks, one of which is to call the system command on "tar -cvf file.tar....." .

This may take some time, so I would like the progress bar to be displayed on the command line, something like the # returned on the screen during a system call.

I did some jerking around and came across a fork . Is this the best way to go? Is it possible to disable the system command and then create a while loop that checks the $pid source code returned by fork?

I also saw links to waitpid .... I assume I need to use this too.

 fork system("tar ... ") while ( forked process is still active) { print # sleep 1 } 

Am I barking the wrong tree?

Thank you so much John

+7
perl
source share
6 answers

Perl has a nice design for this called "pipe opens." You can read about this by typing perldoc -f open at the command line.

 # Note the use of a list for passing the command. This avoids # having to worry about shell quoting and related errors. open(my $tar, '-|', 'tar', 'zxvf', 'test.tar.gz', '-C', 'wherever') or die ...; 

Here is a snippet showing an example:

  open(my $tar, '-|', 'tar', ...) or die "Could not run tar ... - $!"; while (<$tar>) { print "."; } print "\n"; close($tar); 

Replace print "." that prints a hash tag every 10-100 lines or so in order to get a good calibration panel.

+7
source share

An example that does not depend on a child process that writes any kind of output, and just prints a dot about once per second while it is running:

 use POSIX qw(:sys_wait_h); $|++; defined(my $pid = fork) or die "Couldn't fork: $!"; if (!$pid) { # Child exec('long_running_command', @args) or die "Couldn't exec: $!"; } else { # Parent while (! waitpid($pid, WNOHANG)) { print "."; sleep 1; } print "\n"; } 

Although this could probably lead to more error checking, there might already be something better in CPAN. Proc :: Background seems promising for abstracting this work, but I'm not sure how reliable it is.

+6
source share
 $|++; open(my $tar, 'tar ... |') or die "Could not run tar ... - $!"; while ($file=<$tar>) { print "$file"; } print "\n"; close($tar); 

This prints the file names obtained from tar.

+2
source share

I would try something like this

 open my $tar, "tar -cvf file.tar..... 2>&/dev/null |" or die "can't fork: $!"; my $i = 0; while (<$tar>) { if( i++ % 1000 == 0 ) print; } close $tar or die "tar error: $! $?"; 
+1
source share

To show progress during a long-term task, you will find Term :: ProgressBar useful - it performs the "print # to screen" that you describe.

+1
source share

Turning to what Hobbs provided, if you want to return data from a child process to a parent process, you need to have an external channel. I ended up using tempfs because it was as simple as a file, but it did not put IO images on disk.

** Important ** You need to exit the child process, because otherwise the "child" process will continue along with the same script, and you will get two print statements. Thus, in the example below, foreach (@stdoutput) will happen twice, even though it is only once in the script.

 $shm_id = time; #get unique name for file - example "1452463743" $shm_file = "/dev/shm/$shm_id.tmp"; #set filename in tempfs $| = 1; #suffering from buffering print ("Activity Indicator: "); #No new line here defined(my $pid = fork) or die "Couldn't fork: $!"; if (!$pid) { # Child @stdoutput=`/usr/home/script.pl -o $parameter`; #get output of external command open (SHM, ">$shm_file"); foreach (@stdoutput) { print SHM ("$_"); #populate file in tempfs } close (SHM); exit; #quit the child process (will not kill parent script) } else { # Parent while (! waitpid($pid, WNOHANG)) { print ("\#"); # prints a progress bar sleep 5; } } print ("\n"); #finish up bar and go to new line open (SHM, "$shm_file"); @stdoutput = <SHM>; #Now open the file and read it. Now array is in parent close (SHM); unlink ($shm_file); #deletes the tempfs file chomp(@stdoutput); foreach (@stdoutput) { print ("$_\n"); #print results of external script } 
0
source share

All Articles