/ / Perl - Вихід із системи / команда backticks на натисканні клавіші, якщо це потребує тривалого часу - perl, foreach, system, alarm, backticks

Perl - виламання системи / зворотного зв'язку натисканням клавіші, якщо воно займає багато часу - perl, foreach, system, alarm, backframes

У мене є проблема, я сподіваюся, що хтось може допомогти з ...

У мене є цикл 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