views:

1614

answers:

8

I have a Perl script that contains this code snippet, which calls the system shell to get some files by SFTP and unzip them with WinZip:

#  Run script to get files from remote server
system "exec_SFTP.vbs";

#  Unzip any files that were retrieved
foreach $zipFile (<*.zip>) {
    system "wzunzip $zipFile";
}

Even if some files are retrieved, they are never unzipped, because by the time the files are retrieved and the SFTP connection is closed, the Perl script has already completed the unzip step, with the result that it doesn't find anything to unzip.

My short-term fix is to insert

sleep(60);

before the unzip step, but that assumes that the SFTP connection will finish within 60 seconds, which may sometimes be a gross over-estimate, and other times an under-estimate.

Is there a more sound way to cause Perl to pause until the SFTP connection is closed before proceeding with the unzip step?

Edit: Responders have questioned (and reasonably so) the use of a VB script rather than having Perl do the file transfer. It has to do with security -- the VB script is maintained by others and is authorized to do the SFTP.

+1  A: 

Have a look at IPC::Open3

ennuikiller
+8  A: 

Check the code in your *.vbs file. The system function waits for the child process to finish before execution continues. It appears that your *.vbs file is forking a background task to do the FTP and returning immediately.

Michael Carman
+4  A: 

You need a way for Perl to wait until the SFTP transfer is done, but as your script is currently written, Perl has no way of knowing this. (It looks like you're combining at least two scripting languages and a (GUI?) SFTP client; this can work, but it's not exactly reliable or robust. Why use VBscript to start the SFTP transfer?)

I can think of four options:

  1. Your Perl script could do the SFTP transfer itself, using something like CPAN's Net::SFTP module, rather than spawning an external job whose status it cannot track.
  2. Your Perl script could spawn a command-line SFTP utility (like PSFTP) that doesn't return until the transfer is done.
  3. Or change exec_SFTP.vbs script to not return until the transfer is done.
  4. If you're currently using a graphical SFTP client and can't switch for whatever reason, I'd recommend using a scripting language like AutoIt instead of Perl. AutoIt has features to wait for windows to change state and so on, so it could more easily monitor for an activity's completion.

Options 1 or 2 would be the most robust and reliable.

Josh Kelley
+2  A: 

The best I can suggest is modifying exec_SFTP.vbs to exit only after the file transfer is complete. system waits for the program it called to complete, so that should solve your problem:

       system LIST
       system PROGRAM LIST
               Does exactly the same thing as "exec LIST", except
               that a fork is done first, and the parent process
               waits for the child process to complete.
Nathan Fellman
+1  A: 

system will not return until the shell it's running the command in has returned; this may be wrong for launching graphical programs and file associations.

See if any of the following help?

system('cscript exec_SFTP.vbs');

use Win32::Process;
use Win32;
Win32::Process::Create(my $proc, 'wscript.exe',
        'wscript exec_SFTP.vbs', 0, NORMAL_PRIORITY_CLASS, '.');
$proc->Wait(INFINITE);
ephemient
+2  A: 

Maybe this is a dumb question, but why not just use the Net::SFTP and Archive::Extract Perl modules to download and unzip the files?

R. Bemrose
+5  A: 

In a perfect world your script would be rewritten to use Net::SFTP::Foreign and Archive::Extract..

An ugly quick-hackish kind of way might be to create a touch-file before your first system call, alter your sftp-fetching script to delete the file once it is done and have a while like so

while(-e 'touch.file') {
    sleep 5;
}

# foreach [...]

Of course, you would need to take care if your .vbs fails and leaves the touchfile undeleted and many other bad side effects. This would be for a quick solution (if none of the other suggestions work) until you get the time to rewrite without system() calls.

Anon
Quick and Dirty, i ilike it.
Zenshai
+2  A: 

If you can't modify the vbs script to stay alive until it terminates, you may be able to track subprocess creation. If you get subprocess ids, you can monitor them thereby know when the vbs' various offspring terminate.

Win32::Process::Info lets you get a subprocess ids from a running process.

daotoad