Bijan
Bijan

Reputation: 8594

Perl: Move to next item in loop before it is done executing

I have a perl script that is like so:

foreach my $addr ('http://site1.com', ...., 'http://site2.com') {
    my $script = `curl -m 15 $addr`;
    *do stuff with $script*
} 

The -m sets a timeout of 15 seconds. Is there a way to make it if a user pushes a key, it stops the current execution and moves onto the next item in the foreach? I know last; can move to the next item but I am unsure of how to link this to the key being pushed and how to do it while the curl script is running

Edit: So based on the answers it seems difficult to do it while curl is running. Would it be possible to push a key while curl is running and have it skip to the next item in the loop as soon as the curl script returns (or times out after 15sec)?

Upvotes: 1

Views: 280

Answers (2)

Sobrique
Sobrique

Reputation: 53478

The problem you've got with this, is that when you run curl perl hands over control and waits for completion. It blocks until it's 'done'.

So it's not as easy to do this as it might seem.

As another poster alludes to - you can use a variety of parallel processing options. I would suggest the easiest is to move away from using 'any' key, and require a ctrl-c.

So you'd then do:

foreach my $addr ('http://site1.com', ...., 'http://site2.com') {
     my $pid = open ( my $curl_fh, "-|", "curl -m 15 $addr" );
     $SIG{'INT'} = sub { print "Aborting fetch of $addr"; kill $pid };
     while ( <$curl_fh> ) {
         print;
     }
     #might want to set it to something else. 
     #undef means 'ctrl-c' will abort the whole program. 
     #IGNORE means exactly what it says on the tin.
     #important to change it though, as it has a specific pid it'll kill, 
     #and that might cause problems. 
     $SIG{'INT'} = undef;
} 

What this does is configure SIGINT (e.g. ctrl-c) so it doesn't kill your program, but does kill the sub-process.

If you wanted to look at other options, I'd offer:

  • Multithreading, spawn a thread to 'do' the curl fetching in the background and use Thread::Queue to pass results back and forth. (Thread::Queue supports nonblocking checks).

  • Forking - fork a sub process to do the curl, and use your 'main' process to send a signal if a key is pressed.

  • IO::Select such that you're not making blocking reads on your process.

Upvotes: 4

rubikonx9
rubikonx9

Reputation: 1413

Basically you have two options:

1. Use threads

Create a new thread, call desired system function there. Wait for output. In another thread, check for user input. On input, you can kill the child process. When child process has finished, you can ignore user input.

Such a solution seems to be rather complex, with a lot of synchronization needed, probably with using signals. Risky.

2. Use non-blocking IO

Please read this thread. It explains how to make non-blocking IO reads from either a file or a pipe. You'd like to make a non-blocking read from pipe (created with open), then non-blocking read from STDIN, loop.

Seems like a way to go, but, alas, rather complex as well.

Upvotes: 2

Related Questions