我需要运行一个Bash命令,但是这个命令需要几分钟才能运行完毕。
如果我正常执行这个命令(同步执行),我的应用程序会一直等待命令完成。
如何在Perl脚本中异步运行Bash命令?
我需要运行一个Bash命令,但是这个命令需要几分钟才能运行完毕。
如果我正常执行这个命令(同步执行),我的应用程序会一直等待命令完成。
如何在Perl脚本中异步运行Bash命令?
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
并重定向STDIN
和STDOUT
(以及STDERR
)。或者您可以使用IPC::Open2或IPC::Open3包。无论如何,在调用程序退出之前等待子进程退出始终是一个好习惯。#!/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
cpid
的数组中。然后有一个循环,它浏览它们仍在运行(/proc/存在)。如果不存在,则显示文本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循环大大减少。
open(my $fh, '|-', 'wait.sh', ...)
或 open(my $fh, '-|', 'wait.sh', ...)
,如果你想要读取其输出或写入其输入,但不需要两者都有。 - reinierpost线程
来异步启动Bash,use threads;
my $t = async {
return scalar `.. long running command ..`;
};
然后手动测试线程是否准备好加入,并以非阻塞的方式获取输出。
my $output = $t->is_joinable() && $t->join();
async
行之后使用$t
,那么perl会返回一个错误:Global symbol "$t" requires explicit package name
。在第一次使用$t
之前加上sleep 1;
可以解决这个问题,但是有没有更好的处理方法呢? - TrueYmy $t = async { return `./wait.sh`; }
print $t;
如果在这两行代码之间添加`sleep 1`,那么它就可以正常工作。
- TrueYPerl exited with active threads:
的问题,但你不需要使用 sleep
来避免这种情况,因为 my $foo = $t->join();
会阻塞直到线程准备好加入。 - mpapec$t->join;
也不起作用,因为$t
不可见(相同的错误消息)。在cygwin
和CentOS 5.7
下也是如此。 - TrueY$t->join
在$t
的作用域内吗? - aschepler通常的做法是使用 fork
。你需要让你的脚本进行分叉,然后子进程会调用 Bash 脚本上的 exec
或 system
命令(具体取决于子进程是否需要处理 Bash 脚本的返回码或与其交互)。
然后,父进程可能需要结合使用 wait
和/或 SIGCHILD
处理程序。
如何处理它的具体细节很大程度上取决于你的情况和确切需求。