/ / Perl - Abbandono di un comando di sistema / backtick su keypress se impiega molto tempo - perl, foreach, system, alarm, backticks

Perl - Interruzione di un comando di sistema / backticks su keypress se richiede molto tempo - perl, foreach, system, alarm, backticks

Ho un problema, spero che qualcuno possa aiutarti con ...

Ho un ciclo foreach che esegue un backtickcomando su ogni iterazione, come greping di una cartella in una directory per una stringa (come mostrato di seguito, notevolmente semplificato allo scopo di spiegare la mia domanda).

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 $foldern";
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 "@outputn";
}
}

Il problema che sto riscontrando:

Se la cartella è il comando grep backtickscorrere contro è particolarmente grande o l'array di parole da verificare è particolarmente grande, quindi il comando backticks potrebbe richiedere ore per il completamento (il che va bene).

Ma quello che voglio essere in grado di fare è scoppiaredel ciclo interno (cioè quando una parola viene usata in una cartella) e passa alla successiva iterazione se impiega molto tempo quando l'utente preme un tasto sulla tastiera o inserisce la parola "successivo" o "esci" per esempio.

So che se non usassi i backtick potreiuscire facilmente da un normale ciclo usando qualcosa di simile al seguente (ma la logica di questo ovviamente non funziona quando è coinvolto un backtick / chiamata di sistema):

use strict;
use warnings;

use Term::ReadKey;

my $n = 0;

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

print $n;

Potrebbe esserci una soluzione semplice che sto trascurando, ma non ho mai avuto bisogno di farlo prima, quindi il tuo aiuto è molto apprezzato, grazie

risposte:

2 per risposta № 1

La soluzione è eseguire il programma di lunga duratain un processo in background (e ricordare l'id del processo del nuovo processo) e mantenere l'interazione dell'utente nel processo in primo piano. Quando viene segnalato l'interruzione del primo piano, interrompere il processo in background.

Tutte le parti che ho citato sono ben spiegate nei post precedenti su Stack Overflow.


1 per risposta № 2

Stai tentando di eseguire contemporaneamente un dispositivo esternocomando ed elaborazione degli eventi della tastiera, quindi è necessario utilizzare un framework asincrono. I framework asincroni si basano su fork, thread o loop di eventi e i loop di eventi non sono appropriati in questo caso.

Ecco una descrizione di come potresti usare una forcella:

use POSIX ":sys_wait_h";  # defines WNOHANG

foreach my $folder (@folderList) {
foreach my $word (@wordList) {
print "Searching for this $word in this $foldern";

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"s 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;
}
}
}
}
}

0 per risposta № 3

Capisco cosa hanno detto le persone in merito a processi in background, thread e fork e così via, ma l'opzione più adatta alla mia disposizione (ed è probabilmente il più facile da implementare), anche se confesso che potrebbe non essere il modo più efficiente, la migliore pratica o il modo preferito di farlo, coinvolto usando eval e catturando i tasti di controllo-c dell'utente.

Esempio molto semplice:

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