Search code examples
perlforeachsystemalarmbackticks

Perl - Breaking out of a system/backticks command on keypress if it takes a long time


I have a problem I am hoping someone can help with...

I have a foreach loop that executes a backticks command on each iteration, such as greping a folder in a directory for a string (as shown below, greatly simplified for the purposes of explaining my question).

my @folderList = ("/home/bigfolder", "/home/hugefolder", "/home/massivefolder");
my @wordList = ("hello", "goodbye", "dog", "cat");

foreach my $folder (@folderList) {
     foreach my $word (@wordList) {
          print "Searching for this $word in this $folder\n";
          my @output = `grep -R $word $folder`;    #this could take hours so the user needs the option to skip/cancel this iteration and go the next one 
          print "@output\n";
     }
}

The problem I am having:

If the folder the backticks grep command is being run against is particularly large or the array of words to check against is particularly large then the backticks command could take hours to complete (which is fine).

But what i want to be able to do is to break out of the inner loop (i.e when a word is being greped for in a folder) and go to the next iteration if it is taking a long time when the user presses a key on the keyboard or enters the word "next" or "exit" for example.

I know that if i wasnt using backticks I could easily break out of a normal loop using something like the following (but the logic of this obviously does not work when a backticks/system call is involved):

use strict;
use warnings;

use Term::ReadKey;

my $n = 0;

while () {
    print '.';
    last if ReadKey(-1);
    $n++;
}

print $n;

There may be a simple solution that I am overlooking but I have never had the need to do this before, so your help is much appreciated, thanks


Solution

  • I understand what people have said regarding background processes, threads and forking and so on, but the option that suited my arrangement the best (and is probably the easier to implement), although I confess may not be the most efficient, best practice or preferred way of doing it, involved using eval and catching user control-c keypresses.

    Very Simple Example:

    NEXT:foreach $folder (@folders) {     #label on the foreach
    
             eval {
                   $SIG{INT} = sub { break() };   #catches control-c keypress and calls the break subroutine
                   $var1 = `grep -r "hello" $folder`;
             };
    
             sub break {
                  print "Breaking out of the backticks command and going to next folder \n";
                  next NEXT;  
             }
    
         } #ending bracket of foreach loop