我有个问题,希望有人能帮忙。。。
我有一个foreach循环,它在每次迭代中执行一个backticks命令,例如在目录中的文件夹中为字符串greping(如下所示,为了解释我的问题,大大简化了)。
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";
}
}
我遇到的问题:
如果运行backticks grep命令所针对的文件夹特别大,或者要检查的单词数组特别大,则backticks命令可能需要数小时才能完成(这很好)。
但我想做的是,如果用户按下键盘上的键或输入单词"next"或"exit"时需要很长时间,那么就打破内部循环(即在文件夹中查找单词时),进入下一次迭代。
我知道,如果我不使用backticks,我可以很容易地使用以下内容来打破正常循环(但当涉及backticks/系统调用时,这种逻辑显然不起作用):
use strict;
use warnings;
use Term::ReadKey;
my $n = 0;
while () {
print '.';
last if ReadKey(-1);
$n++;
}
print $n;
可能有一个简单的解决方案我忽略了,但我以前从未需要这样做,所以非常感谢您的帮助,感谢
解决方案是在后台进程中运行长期运行的程序(并记住新进程的进程id),并将用户交互保持在前台进程中。当前台发出中断信号时,终止后台进程。
我提到的所有部分在之前关于Stack Overflow的文章中都有很好的解释。
您正试图同时运行外部命令和处理键盘事件,因此需要使用一些异步框架。异步框架基于fork、线程或事件循环,在这种情况下不适合使用事件循环。
以下是如何使用叉子的概述:
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;
}
}
}
}
}
我理解人们对后台进程、线程和分叉等的看法,但最适合我的安排的选项(可能更容易实现),尽管我承认可能不是最有效、最佳实践或首选的方法,包括使用eval和捕获用户控制键。
非常简单的示例:
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