【问题标题】:Perl: Unable to execPerl:无法执行
【发布时间】:2024-01-06 04:41:01
【问题描述】:

我正在尝试在不同平台上并行运行几个子进程。只有在所有子进程在各自平台上完成后,父进程才应继续进行。

问题是当我使用 fork 然后在子进程中运行“exec”命令时,它几乎立即结束。此外,输出不一致。几乎每次日志只显示一行。

-bash-2.05b$ cat Agent.SOLSPARC
caught SIGTERM signal, cleaning up

-bash-2.05b$ cat Agent.SOLSPARC
Host: EBSO9SPC  Login: esm2 

有时,有几行多余的行,最后是消息,'被信号 15 杀死'。我在“exec”中使用的命令实际上调​​用了一个脚本,该脚本连接到远程盒子并在它们上运行 make 命令。出于测试目的,我目前只通过一个平台,即 SOLSPARC。另外,我只想知道命令是否在任何给定平台上完成。

我不确定我是否正确地将所有参数传递给了“exec”,所以我尝试了不同的组合(在参考了 Internet 上的不同链接之后)但无济于事。一个重要的观察结果是,当我使用 strace 调试此问题时,该命令运行良好。我在 perldoc 中看到 exec 在 Unix 平台上使用 /bin/sh -c ,但在其他平台上有所不同。是不是exec和strace使用了不同的shell?

这是我的代码的相关部分:

sub compile {
    my %child_pids;
    foreach $plat (0 .. $#plat_list) {
        my $pid = fork;
        # Didn't check the undef condition for child
        if ($plat_list[$plat] eq "SOLSPARC") {
            print "\nStarted Solaris build \n";
            if ($pid == 0) {
                print "Inside Child Process \n\n";
                exec ( "${ROOT}/${REM_EXEC} -t 1200 -c \"make LANG=en_US distclean \" -b ${ROOT} -l Agent. $plat_list[$plat]" ) or die "exec failed";
            } elsif ($pid > 0) {
                $child_pids{"SOLSPARC"} = $pid;
            }
        } else {
           print "\nStarted build for other platforms \n";
           if ($pid == 0) {
                print "Inside Child Process \n\n";
                exec ( "${ROOT}/${REM_EXEC} -t 1200 -c \"make LANG=en_GB clean \" -b ${ROOT} -l Agent. $plat_list[$plat]" ) or die "exec failed";     
           } elsif ($pid > 0) {
                $child_pids{"$plat_list[$plat]"} = $pid;
           }
        }
    }


    my %rev_child_pids = reverse %child_pids;
    while ((my $kid = waitpid -1, WNOHANG) > 0) {
        if ($rev_child_pids{$kid} eq "SOLSPARC") {
            print "\nChild process completed for SOLARIS platform $rev_child_pids{$kid} \n";
            print "Run some other command here \n";
        } else {
            print "\nChild process completed for other platform $rev_child_pids{$kid} \n";
            print "No more commands to run \n";
        }
    }
}

有什么建议吗?

【问题讨论】:

  • 您可能应该使用exec 的“列表形式”,传入参数列表而不是单个字符串。这样可以避免执行 shell 来运行命令。
  • 只是添加到乔纳森的注释中,只有当你的命令行参数没有任何特殊的 shell 字符时,避免 shell 才有效,例如<| 等...
  • @DVK 是对的,但还有一个安全问题,如果你不小心${ROOT}${REM_EXEC} 等变量,你最终可能会在你的代码。对于命令行脚本来说,这似乎不是什么大问题,但请考虑一下:假设${ROOT} 是您自动获取的文件系统上的文件名,而该文件恰好被命名为“;rm -rf /*; ",你可能对结果不满意。使用多参数 exec 可以避免这个特定问题。
  • 我尝试了以下两种形式,但输出仍然没有变化。 exec ( "${ROOT}/${REM_EXEC}", "-t 1200", "-c \"make LANG=en_US distclean \"", "-b ${ROOT}", "-l Agent.", "$plat_list[$plat]" ) or die "exec failed";exec ( "${ROOT}/${REM_EXEC}", "-t", "1200", "-c", "\"make LANG=en_US distclean \"", "-b", "${ROOT}", "-l", "Agent.", "$plat_list[$plat]" ) or die "exec failed"; 我以前也试过这些表格。
  • 您需要向我们展示重现症状的最小完整代码。 (例如,您是否在某处生成 nmap?这可以解释“caught SIGTERM ...”消息。)

标签: perl exec fork


【解决方案1】:

尝试使用“系统”而不是“执行”。

system `${ROOT}/${REM_EXEC} -t 1200 -c "make LANG=en_US distclean " -b ${ROOT} -l Agent. $plat_list[$plat]` 

'system' 与 fork 的工作方式略有不同,因此它可能会解决问题。

【讨论】:

    最近更新 更多