У мене є проблема, я сподіваюся, що хтось може допомогти з ...
У мене є цикл foreach, який виконує backticksкоманда на кожну ітерацію, наприклад, зіставлення папки в каталозі для рядка (як показано нижче, значно спрощена для пояснення мого питання).
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";
}
}
Проблема, яка у мене виникає:
Якщо в папці виконується команда grep backticksБіг проти особливо великий, або масив слів, щоб перевірити, є особливо великим, тоді команда backticks може зайняти години (що нормально).
Але те, що я хочу вміти, - це вирватисявнутрішнього циклу (тобто, коли слово папкується в папці) і перейдіть до наступної ітерації, якщо це забирає тривалий час, коли користувач натискає клавішу на клавіатурі або вводить слово "наступний" або "вихід" наприклад.
Я знаю, що якби я не використовував backticks, я міг билегко вийти з звичайного циклу, використовуючи щось на кшталт наступного (але логіка цього, очевидно, не працює, коли беруть участь backticks / системний виклик):
use strict;
use warnings;
use Term::ReadKey;
my $n = 0;
while () {
print ".";
last if ReadKey(-1);
$n++;
}
print $n;
Можливо, є просте рішення, яке я не помічаю, але у мене ніколи не було потреби цього робити, тому ваша допомога дуже вдячна, дякую
Відповіді:
2 для відповіді № 1Рішення полягає у запуску тривалої програмиу фоновому процесі (і запам’ятайте ідентифікатор процесу нового процесу), а також підтримуйте взаємодію користувачів у процесі переднього плану. Коли передній план сигналізує про розрив, вбийте фоновий процес.
Усі згадані мною частини добре пояснені в попередніх дописах на стеку Overflow.
1 для відповіді № 2
Ви намагаєтеся одночасно запустити зовнішнюкомандувати та обробляти події клавіатури, тому вам потрібно використовувати деякий асинхронний фреймворк. Асинхронні рамки базуються на вилках, потоках або циклах подій, і петлі подій у цьому випадку не підходять.
Ось контур того, як можна використовувати вилку:
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 для відповіді № 3
Я розумію, що люди сказали про фонові процеси, потоки та розгортання тощо, але варіант, який найкраще підходив моїй компоновці (і це, мабуть, простіше у виконанні), хоча, я вважаю, це може бути не найефективнішою, найкращою практикою чи бажаним способом цього, що стосується використання eval та лову користувальницьких клавіш control-c.
Дуже простий приклад:
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