Perl脚本并行启动bash脚本并获取各进程退出码方案
解决Perl并行启动进程并获取每个进程退出码的问题
你的需求完全可以通过Perl的waitpid函数实现,它能非阻塞地跟踪子进程的退出状态,比单纯用kill 0检查存活状态更可靠,还能直接解析出每个进程的退出码或终止信号,正好对应你想要的类似PHPproc_get_status的功能。
核心思路
- 使用
waitpid($pid, WNOHANG)进行非阻塞检查:不会阻塞主进程的循环,同时能判断进程是否结束 - 通过POSIX提供的状态解析函数(
WEXITSTATUS、WIFSIGNALED等),从$?中提取具体的退出码或终止信号 - 处理完结束的进程后,记得关闭文件句柄并从进程数组中移除,避免资源泄漏和无效检查
修改后的完整代码
首先需要导入POSIX模块的相关常量,然后调整进程监控逻辑:
use warnings; use strict; use IPC::Open3; use IO::Handle; use POSIX qw(:signal_h :sys_wait_h); # 导入wait相关常量 my $timeLimit = 60*60; # 时间限制 my $startTime = time(); my @commands = ('bash script1.sh', 'bash script2.sh'); # 示例命令 my @processes = (); foreach my $cmd (@commands) { my $stdout = IO::Handle->new; my $stderr = IO::Handle->new; # 注意:你原来的代码里用了$fullcmd,但定义的是$cmd,这里统一成$cmd my $pid = open3(undef, $stdout, $stderr, $cmd); push @processes, { pid => $pid, out => $stdout, err => $stderr, cmd => $cmd }; } do { if (time() - $startTime > $timeLimit) { # 超时:给所有进程发送SIGINT信号 kill 2, $_->{pid} foreach (@processes); @processes = (); last; } else { # 遍历进程数组,检查每个进程状态 for (my $i = 0; $i < @processes; $i++) { my $proc = $processes[$i]; my $pid = $proc->{pid}; # 非阻塞等待进程结束 my $wait_result = waitpid($pid, WNOHANG); if ($wait_result > 0) { # 进程已结束,解析退出状态 my $exit_status = $?; if (WIFEXITED($exit_status)) { # 进程正常退出,获取退出码 my $exit_code = WEXITSTATUS($exit_status); print "Command '$proc->{cmd}' (PID $pid) exited normally with code: $exit_code\n"; # 这里可以添加日志记录逻辑 } elsif (WIFSIGNALED($exit_status)) { # 进程被信号终止,获取信号编号 my $signal = WTERMSIG($exit_status); print "Command '$proc->{cmd}' (PID $pid) was killed by signal: $signal\n"; } # 关闭stdout/stderr句柄,释放资源 close $proc->{out}; close $proc->{err}; # 从数组中移除已结束的进程 splice(@processes, $i, 1); $i--; # 数组长度变化,调整索引避免跳过下一个进程 } elsif ($wait_result == -1) { # waitpid出错(比如PID不存在),直接移除该进程 print "Error waiting for PID $pid: $!\n"; splice(@processes, $i, 1); $i--; } # wait_result == 0 说明进程仍在运行,无需处理 } } } while (@processes > 0);
关键细节说明
为什么不用
kill 0?kill 0只能检查进程是否存在,但如果进程已经变成僵尸进程(父进程未调用wait收割),kill 0会返回true,但实际上进程已经结束。而waitpid会自动收割僵尸进程,同时获取真实的退出状态,是更可靠的方式。状态解析函数的作用
WIFEXITED($exit_status):判断进程是否正常退出WEXITSTATUS($exit_status):获取正常退出的退出码(范围0-255)WIFSIGNALED($exit_status):判断进程是否被信号终止WTERMSIG($exit_status):获取终止进程的信号编号
文件句柄的关闭
用open3创建的stdout/stderr句柄必须手动关闭,否则会导致资源泄漏,尤其是在长时间运行的脚本中。
内容的提问来源于stack exchange,提问作者arunoruto
相关产品推荐
相关产品推荐

