Perl - turn off the system / backticks command when a key is pressed, if it takes a long time

I have a problem, I hope someone can help with ...

I have a foreach loop that executes the backticks command at each iteration, for example, greping in the directory for the string (as shown below, it is much easier to explain 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 facing:

If the folder in which the backticks grep command is executed is especially large or the array of words to check is especially large, then the backticks command can take several hours (this is normal).

But what I want to do is to exit the inner loop (i.e. when the word is copied to the folder) and go to the next iteration if it takes a long time when the user presses a key on the keyboard or, for example, enter the word " next "or" exit ".

I know that if I had not used backticks, I could easily break out of the normal loop using something like the following (but the logic of this obviously does not work when callbacks / system call are 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 skip, but I never had to do this before, so your help is much appreciated, thanks

+4
source share
3 answers

I understand that people talked about background processes, threads and markup, etc. , but the one that suits my layout is the best (and probably easier to implement), although I admit that not being the most efficient, best practice or preferred way of doing it, including using eval and catching custom control keys with c keys.

A 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 
0
source

The solution is to run a long-term program in the background process (and remember the process ID of the new process) and maintain user interaction in the foreground process. When the foreground signals a break, kill the background process.

All the parts I mentioned are well explained in previous stack overflow posts.

+2
source

You are trying to run external commands and handle keyboard events at the same time, so you need to use some asynchronous structure. Asynchronous frameworks are based either on views, streams, or on event loops, in which case event loops are not suitable.

It describes how to use the plug:

 use POSIX ':sys_wait_h'; # defines WNOHANG foreach my $folder (@folderList) { foreach my $word (@wordList) { print "Searching for this $word in this $folder\n"; my $pid = fork(); if ($pid == 0) { # child process # we are just printing output from the child process; if you want # to move data from the child process back to the parent, well, # that a whole other can of worms print `grep -R $word $folder`; exit; } else { # parent process while (waitpid($pid, &WNOHANG) != $pid) { if (Term::ReadKey(-1)) { kill 'TERM', $pid; # or maybe kill 'KILL', ... last; } } } } } 
+1
source

Source: https://habr.com/ru/post/1494091/


All Articles