5

这真让我抓狂。我正在尝试在我的 PHP Web 应用程序的 Windows 框中执行命令行语句。它在 Windows XP、IIS5.1 上运行。Web 应用程序运行良好,但我无法让 @exec() 使用特定的联系人变量。我的命令结构如下所示:

$cmd = ($config->svn." cat ".$this->repConfig->svnParams().quote($path).' -r '.$rev.' > '.quote($filename));

当它生成以下字符串时,此命令不能像上面那样工作:

svn --non-interactive --config-dir /tmp cat "file:///c:/temp/test/acccount/dbo_sproctest.sql" -r 1 > "C:\Inetpub\sites\websvn\temp\wsv5B45.tmp"

如果我将它复制/粘贴到我自己的命令行,它工作正常。

如果我对相同的路径进行硬编码而不是将其与变量一起添加,它就可以工作!我试过在文件名周围加引号和不加引号。我试过在整个命令周围加引号和不加引号。我试过其他目录。我尝试将输出参数传递给 exec(),但它返回为空(Array ())。我尝试将命令的错误流的输出重定向到一个文件,并且永远不会创建该错误输出文件。

我唯一能想到的是 exec() 默默地失败了。我到底在这里做错了什么?如果我使用相同的目录结构和文件名对文件路径进行硬编码,它就可以正常工作。如果我不这样做,它不会。

也许文件路径中的斜杠 () 没有被正确转义,但是当我用单引号手动执行时,它们不被视为转义序列?

更新:

我从 exec 中删除了@,但仍然没有看到任何错误。

我给出了 SVN 的完整路径,仍然没有运气。应该注意的是,只要我手动指定 cat 的文件目标,该命令在使用非完整路径 SVN 之前运行良好。

更新 2:回复:基思

我通过尝试两种方法来调用 exec:

exec($cmd);

或者

exec($cmd, $out);

我的 php.ini 已经有了 safe_mode = 0。

我添加了 error_reporting(E_ALL); 并没有看到任何新东西

如果我回显(或 print_r)我的 exec 调用,我实际上并没有看到任何东西

如果我在包含输出变量时回显(或 print_r)我的 exec 调用,我会得到一个空的 arr

更新 3

我尝试了 escapeshellcmd 和 escapeshellarg 都无济于事(不过是个好主意)。

我应该补充一点,文件是通过调用创建的

tempnam("temp", "wbsn");

如果我手动指定字符串而不是让它由 tempname 生成,它工作得很好这一事实似乎表明问题的根源,但我不知道如何。我将手动字符串与生成的字符串进行了比较,它作为匹配返回。

4

9 回答 9

23

@exec总是会默默地失败,因为 @ 是 PHP 的错误抑制运算符。

于 2009-04-03T16:43:34.877 回答
6

不确定这是否会对您有所帮助,因为您使用的是 Windows,而我使用的是 Linux,但我遇到了来自 PHP exec() 的同样的静默错误问题。我发现我试图发出的命令 (nconvert) 将其错误消息发送到标准错误流,而不是标准输出。所以我加了

2>&1

在命令行的末尾将我的错误重定向回标准流。然后我可以看到 nconvert 给了我一个权限被拒绝的错误。

于 2010-02-16T17:28:33.130 回答
2

可能是 PATH 与您的 php 脚本与您的用户帐户不同。尝试删除 @ 并查看它是否试图引发错误。

此外,您可能想尝试将完整的文件系统路径放入 SVN 可执行文件。

于 2009-04-03T16:42:46.187 回答
1

在调试 shell exec 问题时,一个非常有用的技巧是在命令的开头放置一个“echo”。确保您可以在某处查看标准输出。

这将让您检查命令是否存在任何明显的问题。也许它有一个意外扩展的通配符,或者外壳引用不完全正确。echo 会让您看到这一点,您可以将 echoed 命令剪切并粘贴到另一个 shell 中,以查看它是否正常工作。

于 2009-08-01T16:52:52.127 回答
1

好吧,如果删除@不起作用,请尝试在您正在运行的代码的开头包含此内容:

error_reporting(E_ALL);

这将打开完整的错误报告,以防万一您在php.ini文件中将其关闭或禁用。

我不认为你能告诉我们你到底是怎么打电话的exec()?您是否还可以检查以确保您没有意外在安全模式下运行脚本?您是否正在回显exec()正在返回的内容,如果是,返回的内容是什么?

于 2009-04-03T17:57:18.193 回答
1

您是否尝试过 echo exec("dir") 或其他简单的方法来查看 exec() 是否正常工作?

于 2009-04-03T19:37:32.243 回答
1

我不知道发生了什么,但我至少有一个解决方法。

这有效:

$tmp = tempnam("./", "wbsn");
$filename = dirname($tmp).'\\temp\\'.basename($tmp);

然而,这并没有,但我希望它生成相同的路径(差异文件名,因为它是一个新的 tempnam())。

$tmp = tempnam("temp", "wbsn");

此外,这不起作用,我也希望生成同样的东西:

$tmp = tempnam("temp", "wbsn");
$filename = dirname($tmp).'\\'.basename($tmp);

所有这 3 个解决方案似乎都生成了相同的文件路径,但只有第一个在我的 exec 中使用时真正起作用。我知道为什么它没有。

所有这三个的目视检查(回声)似乎生成了相同的路径(当然,文件名不同)。这 3 个中每一个的 dirname() 的字符串比较显示为匹配。我不知道这笔交易是什么,但第一个是一种解决方法。

于 2009-04-03T19:52:36.013 回答
1

我不太喜欢添加另一个回复,但如果我只是编辑我之前的回复,您可能看不到它。

PHP 有一些特殊的 shell 脚本转义命令:escapeshellcmdescapeshellarg

认为您应该能够escapeshellcmd在整个 $cmd 周围使用,但我不确定。

于 2009-04-03T18:41:48.290 回答
-1

我的建议是从 WIN、IIS 切换到 linux,但作为替代方案,你可以试试这个:

function exec_alt($cmd) {
    exec($cmd, $output);
    if (!$output) {
        /**
         * FIXME: for some reason exec() returns empty output array @mine,'s machine.
         *        Somehow proc_open() approach (below) works, but doesn't work at
         *        test machines - same empty output with both pipes and temporary
         *        files (not we bypass shell wrapper). So use it as a fallback.
         */
        $output = array();
        $handle = proc_open($cmd, array(1 => array('pipe', 'w')), $pipes, null, null, array('bypass_shell' => true));
        if (is_resource($handle)) {
            $output = explode("\n", stream_get_contents($pipes[1]));
            fclose($pipes[1]);
            proc_close($handle);
        }
    }
    return $output; }
于 2013-03-26T16:32:09.087 回答