2013-05-27 81 views
2

我必须运行Bash命令。但是这个命令需要几分钟才能运行。从Perl脚本异步执行Bash命令

如果我正常(同步)执行此命令,我的应用程序将挂起,直到命令完成运行。

如何从Perl脚本异步运行Bash命令?

+1

请参阅[我如何在Perl中触发并忘记进程?](http://stackoverflow.com/questions/2133910/how-can-i-fire-and-forget-a-process-in-perl) – devnull

+0

你关心bash输出吗? –

+0

是的。我想要结果,什么和什么时候?像这样:当命令完成时,它会提醒我的应用程序,如事件触发器。 –

回答

2

您可以使用threads异步开始猛砸,

use threads; 
my $t = async { 
    return scalar `.. long running command ..`; 
}; 

后来手动测试,如果线程准备加入,并在非阻塞的方式得到输出,

my $output = $t->is_joinable() && $t->join(); 
+0

+1。我有一个小问题。如果在'async'行使用'$ t'后,perl会返回一个错误:'全局符号'$ t“需要显式包名'。在第一次使用'$ t'之前,'sleep 1;'解决了这个问题,但是有没有适当的方法来处理这个问题呢? – TrueY

+0

你能复制粘贴你的代码吗? –

+0

''my $ t = async {return'。/ wait.sh'; }''print print $ t;'。如果在两条线之间添加“睡眠1”,则它可以正常工作。 – TrueY

2

如果您不关心结果,您可以使用system("my_bash_script &");。它会立即返回,脚本完成需要完成的任务。

我有两个文件:

$ cat wait.sh 
#!/usr/bin/bash 
for i in {1..5}; { echo "wait#$i"; sleep 1;} 

$cat wait.pl 
#!/usr/bin/perl 
use strict; use warnings; 
my $t = time; 
system("./wait.sh"); 
my $t1 = time; 
print $t1 - $t, "\n"; 
system("./wait.sh &"); 
print time - $t1, "\n"; 

输出:

wait#1 
wait#2 
wait#3 
wait#4 
wait#5 
5 
0 
wait#1 
wait#2 
wait#3 
wait#4 
wait#5 

可以看出,第二个电话会立即返回,但它一直写到标准输出。

如果您需要与孩子沟通,则需要使用fork并重定向STDINSTDOUT(和STDERR)。或者您可以使用IPC::Open2IPC::Open3包。无论如何,在呼叫者退出之前等待孩子退出总是一个好习惯。

如果你想等待执行的过程中,你可以尝试使用Bash是这样的:

#!/usr/bin/bash 

cpid=() 
for exe in script1 script2 script3; do 
    $exe& 
    cpid[$!]="$exe"; 
done 

while [ ${#cpid[*]} -gt 0 ]; do 
    for i in ${!cpid[*]}; do 
    [ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i] 
    done 
    echo DO SOMETHING HERE; sleep 2 
done 

这个脚本起初异步启动脚本#并存储在阵列中的PID被称为cpid。然后是一个循环;它浏览它们仍在运行(/ proc/exists)。如果不存在,则显示文本UNSET <PID>,并从阵列中删除PID。

它不是防弹的,就好像DO SOMETHING HERE零件运行时间很长,那么相同的PID可以被添加到另一个过程中。但它在平均环境中效果很好。

但这种风险也可降低:

#!/usr/bin/bash 

# Enable job control and handle SIGCHLD 
set -m 
remove() { 
    for i in ${!cpid[*]}; do 
    [ ! -d /proc/$i ] && echo UNSET $i && unset cpid[$i] && break 
    done 
} 
trap "remove" SIGCHLD 

#Start background processes 
cpid=() 
for exe in "script1 arg1" "script2 arg2" "script3 arg3" ; do 
    $exe& 
    cpid[$!]=$exe; 
done 

#Non-blocking wait for background processes to stop 
while [ ${#cpid[*]} -gt 0 ]; do 
    echo DO SOMETHING; sleep 2 
done 

该版本允许脚本时的异步子进程退出接收SIGCHLD信号。如果收到SIGCHLD,它会异步查找第一个不存在的进程。等待while循环减少了很多。

+0

另一个选项是'open(my $ fh,'| - ','wait.sh',...)'或'open(my $ fh,' - ','wait.sh',... )'如果你想读取它的输出或写入它的输入,但不是两者。 – reinierpost

+0

不,你不了解我@TrueY,我的bash命令需要很多时间才能运行,所以我希望当bash命令执行时,我的应用程序显示文本“正在执行...”,并在完成命令后,系统triiger(通知)我的应用程序显示“执行完成!”。 –

+0

@ThếAnhNguyễn:对不起,我的误解,但你的描述不包含太多的信息。我有一个[所以回答其他问题](http://stackoverflow.com/questions/16772186/bash-parallelize-md5sum-checksum-on-many-files/16773505#16773505)。它在bash中运行特定数量的并行程序。我根据您的需求定制它。 – TrueY

2

正常这样做的方式是fork。您将获得脚本分支,然后子代会在Bash脚本上调用execsystem(取决于孩子是否需要处理Bash脚本的返回代码,或者与其交互)。

然后,您的父母可能需要wait和/或SIGCHILD处理程序的组合。

如何处理它的确切细节取决于您的情况和确切的需求。