Perl-如果需要很长时间,则在按键时中断系统/backticks命令



我有个问题,希望有人能帮忙。。。

我有一个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

最新更新