【问题标题】:exec always returns -1 (or 127)exec 总是返回 -1(或 127)
【发布时间】:2025-11-21 19:50:02
【问题描述】:

我在生产服务器上使用 php 5.2.9,似乎 exec() 函数的行为“非标准”。

如果我运行exec("ls", $output, $return_var),那么$output 将按预期包含当前文件夹中的文件列表,但$return_var 将按预期设置为-1 而不是0。 我正在使用$return_var 来确定命令成功完成的位置,并且在所有其他经过测试的服务器上都按预期工作:)

有人遇到过这样的情况吗?


编辑:

<?php
$command = "asd";

$t1 = time();

$output = Array();
$result = -5;
$r = exec($command, $output, $result);
$t2 = time();

echo "<pre>";
var_export(Array(
    'command'=>$command,
    'result'=>$result,
    'output'=>implode("\n", $output),
    'r'=>$r,
    't2-t1'=>$t2-$t1,
));
echo "</pre>";

无论我在$command$result 中输入什么命令,都将始终为-1,即使对于不存在的命令也是如此……这很奇怪

【问题讨论】:

  • 如果您的$command 变量是一个空字符串而exec() 没有给ls 提供任何参数怎么办?退出码还是-1吗?如果是这样,您是否使用man ls 为当前系统检查了ls 的实现,以查看是否定义了退出代码-1?其他命令也是这样吗(可能是du -h index.html)?
  • 我唯一能看到的是这个post on the exec php manual page,但由于它在其他服务器上工作,所以不清楚为什么会这样。
  • 我看过那个评论,我没有在 php.ini 中使用这些信号。 php 也不是用 --enable-sigchld 编译的,这可能会导致同样的问题(为 php 4.0.. 找到错误)
  • 这很奇怪,我认为 UNIX 样式的退出代码是用无符号字节完成的,因此值在 0 到 255 之间。
  • @R.Bemrose:是的,返回码应该是我所知道的正整数。 -1 不是其中之一。 @Mark B:已经检查过了。请检查 cmets

标签: php linux exec


【解决方案1】:

假设返回 $result == -1 的系统是基于类 Unix 的(我不知道使用相同代码的 Windows 会如何表现)

PHP (5.2.9) exec() 函数不调用 C exec() 原语(如果它无法替换/执行进程,则返回 -1,这里不是这种情况)。相反,它调用 popen() 创建管道,执行 fork() 并使用您的命令执行 shell。 return_value -1 不是 C 原语的直接结果,而是由 PHP 内部构建的,具体取决于您的命令的处理方式。换句话说,“ls”命令可能执行得很好,而例如 PHP 无法正确关闭管道。

查看 ext/standard/exec.c 中的 C 代码,返回代码为 -1 可能有两个原因,由错误触发;第二个发生在 popen() 调用之后

  fp = VCWD_POPEN(cmd_p, "r");

  if (!fp) {
       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to fork [%s]", cmd);
       goto err;
  }
  // ...
  err:

  pclose_return = -1;
  goto done;

但是在这种情况下,您不会看到结果,并且日志会显示错误。

稍后,通过该行设置return_value

  pclose_return = php_stream_close(stream);

看_php_stream_free()(php_stream_close()是用_php_stream_free()代替的宏),最有可能返回-1的候选是

  ret = stream->ops->close(stream, preserve_handle ? 0 : 1 TSRMLS_CC);

这反过来又间接调用了 C 原语 pclose()。根据说明书

如果 wait4(2) 返回错误或检测到其他错误,则 pclose() 函数返回 -1。

在关闭管道期间似乎检测到了一个错误,这并不妨碍设置结果数据。要严格查找原因,需要检查操作系统设置和日志、PHP 配置和编译参数。

我会推荐

  • 为您的操作系统应用补丁,并可能更新到更新的版本(如果适用),
  • 将 PHP 更新到 5.3.3(截至目前为止的最新版本),因为 PHP exec() 代码发生了重大变化。

请注意,5.3 版中与 PHP suhosin 模块相关的更改默认增强了运行 PHP 文件时的安全性。

【讨论】:

  • 是的,它是“类 unix”(老实说,我不知道它是 Linux 还是 unix 风格)。很好的答案。服务器管理员解决了这个问题,但我不知道他做了什么来破坏或修复它......问题是服务器以非常非常严格的方式配置。我只能访问很少的命令(例如 php-cli 不能通过 SSH 使用,还有很多其他命令。
  • 我接受你的回答,因为它是最完整的,我认为服务器管理员已经找到了操作系统。
【解决方案2】:

确保您没有在 safe mode 中运行,并且该 exec 未在 php.ini 中的 disable_functions 中列出。

这两种情况都会导致exec() 失败,但我认为会发出通知。

【讨论】:

  • 我没有在安全模式下运行(你不能在安全模式下使用 exec)并且没有禁用的功能。
  • @Quamis - 我认为它不适用于您的情况,但您可以在安全模式下使用 exec。然后发生的情况是,在启用安全模式的情况下,命令字符串使用 escapeshellcmd() 进行转义。 - php.net/manual/en/function.exec.php
【解决方案3】:

我们可以得到 strace'ing PHP 进程的输出吗?这可能包含我们正在寻找的答案。

5.2.14 也是 5.2 系列中的最新版本。你有机会去那里试试吗?如果您使用的是共享托管服务提供商,您仍然可以让它在本地运行以查看行为是否发生变化。

【讨论】:

    【解决方案4】:

    我在两台不同的 Linux PC(PHP 5.03 和 PHP 5.2.10)上进行了测试——都运行良好。

    PHP 5.2.10 示例:

    array (
      'command' => 'ls',
      'result' => 0,
      'output' => 'atmail
    ...
    vhosts',
      'r' => 'vhosts',
      't2-t1' => 0,
    )
    

    我会仔细检查您的 php.ini 文件中是否存在任何与安全相关的指令,检查您尝试搜索的目录中的文件权限,并查看您是否正在运行 SELinux 和/或 AppArmor。

    你也可以考虑一个替代方案,比如 opendir()/readdir()。

    恕我直言..PSM

    【讨论】:

      【解决方案5】:

      似乎问题已由服务器管理员解决。我不知道他做了什么,但现在可以了。问题是服务器管理员非常“严格”,也许他对某些系统配置有点限制。例如,从 SSH shell 中,我看不到 php 二进制文件的安装位置。我很确定 SSH shell 是 chroot 的,还有网络服务器(要么是完全不同的服务器,要么是完全不同的服务器,但我不知道如果不使用任何类型的挂载,这怎么可能)......

      【讨论】: