/ / Perl - Wyłamanie polecenia system / backticks przy naciśnięciu klawisza, jeśli zajmuje dużo czasu - perl, foreach, system, alarm, backticks

Perl - Wylogowanie się z polecenia system / backticks przy naciśnięciu klawisza, jeśli trwa to długo - perl, foreach, system, alarm, backticks

Mam problem Mam nadzieję, że ktoś może pomóc ...

Mam pętlę foreach, która wykonuje backtickspolecenie na każdej iteracji, takie jak greping folderu w katalogu dla ciągu (jak pokazano poniżej, znacznie uproszczone w celu wyjaśnienia mojego pytania).

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

Problem, który mam:

Jeśli folder, w którym znajduje się polecenie greprun against jest szczególnie duży lub tablica słów do sprawdzenia jest szczególnie duża, a wykonanie polecenia backticks może zająć godziny (co jest w porządku).

Ale to, co chcę zrobić, to wybuchnąćwewnętrznej pętli (tj. gdy słowo jest poszukiwane w folderze) i przejdź do następnej iteracji, jeśli zajmuje dużo czasu, gdy użytkownik naciska klawisz na klawiaturze lub wprowadza słowo „następny” lub „wyjście” na przykład.

Wiem, że gdybym nie używał backticksów, mógłbymłatwo wyłamać się z normalnej pętli używając czegoś podobnego do poniższego (ale logika tego oczywiście nie działa, gdy zaangażowane są backticks / wywołanie systemowe):

use strict;
use warnings;

use Term::ReadKey;

my $n = 0;

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

print $n;

Może być proste rozwiązanie, którego nie dostrzegam, ale nigdy wcześniej nie miałem takiej potrzeby, więc twoja pomoc jest bardzo ceniona, dzięki

Odpowiedzi:

2 dla odpowiedzi № 1

Rozwiązaniem jest uruchomienie długo działającego programuw procesie w tle (i zapamiętaj identyfikator procesu nowego procesu) i utrzymuj interakcję użytkownika w procesie pierwszoplanowym. Gdy pierwszy plan jest sygnalizowany przerwaniem, zabij proces w tle.

Wszystkie wspomniane przeze mnie części są dobrze wyjaśnione w poprzednich postach na Stack Overflow.


1 dla odpowiedzi nr 2

Próbujesz jednocześnie uruchomić zewnętrznąpolecenia i przetwarzanie zdarzeń klawiatury, więc musisz użyć asynchronicznej struktury. Ramy asynchroniczne są oparte na widłach, wątkach lub pętlach zdarzeń, a pętle zdarzeń nie są w tym przypadku odpowiednie.

Oto zarys tego, jak można użyć widelca:

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 dla odpowiedzi № 3

Rozumiem, co ludzie powiedzieli o procesach w tle, wątkach i rozwidleniu itd., ale opcja, która najbardziej odpowiadała mojej aranżacji (i prawdopodobnie jest łatwiejszy do wdrożenia), chociaż przyznaję, że może nie być najskuteczniejszą, najlepszą praktyką lub preferowanym sposobem jej wykonania, wymaga użycia eval i przechwytywania naciśnięć klawiszy kontrolnych użytkownika.

Bardzo prosty przykład:

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