PHP运行时没有输出任何内容

18

我一直在使用exec(),尝试在使用at Unix系统命令添加任务时捕获其输出。我的问题是,当从我的脚本运行时,它没有产生任何输出,然而在终端和PHP交互模式下运行它却打印出了几行。

我想要执行的命令是:

echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);

var_dump()输出 string(0) "",而print_r()则输出 Array ()。我尝试使用shell_exec(),但它的输出是NULL。不过,当在网页上下文中运行以下代码时,会输出hi

echo exec("echo 'hi'");

这也输出一些东西:

echo exec("atq");

然而,一旦我使用 at,就没有任何输出。如何获得以下输出:

exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);

因为目前将其作为“普通”PHP通过Apache运行时没有任何输出,但是在终端和PHP的交互式控制台中运行该命令会给出预期结果,例如:

php > echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
warning: commands will be executed using /bin/sh
job 1219 at Sun Jun 10 12:43:00 2012

safe_mode已关闭,但我不知道为什么使用管道输入echo语句给at命令没有输出,而使用exec()执行atq或其他命令则有输出。我已经搜索和阅读了这个问题,但都没有成功。

我该如何让exec()at命令的输出返回为字符串或者数组(如果使用exec()的第二个参数)?


闻起来像是 seLinux 或其他权限问题。即使没有看到任何输出,任务是否被安排了? - Ja͢ck
是的,即使没有输出,它也会被调度。运行 sudo atq 列出新任务。 - Bojangles
3个回答

34

工作中,一行解决方案

我没有意识到这可以如此简单。所需的全部是将 stderr 重定向到 stdout,方法是在要执行的命令末尾加上 2>&1。现在来自 at 的任何输出都会被打印到 stdout,因此被 exec() 捕获:

echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes 2>&1", $result);

我的旧解决方案:

我一直在尝试保持一种简洁的解决方案,但最终唯一行之有效的方法是使用 proc_open(),因为 at 日志记录到 stderr 中,而 exec() 并不能读取!我要感谢 @Tourniquet 指出这一点,然而他已经删除了他的回答。引用他的话:

据我所见,at 输出到 stderr,exec 并不能捕获到。我不是很有把握,但可以考虑使用 http://php.net/manual/zh/function.proc-open.php,它允许你将 stderr 直接指向其自己的管道。

这实际上是正确的做法。我的解决方案(因为我只想要 stderr)是这样的:

// Open process to run `at` command
$process = proc_open("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", array(2 => array("pipe", "w")), $pipes);

// Get stuff from stderr, because `at` prints out there for some odd reason
if(is_resource($process)) {
    $output = stream_get_contents($pipes[2], 100);
    fclose($pipes[2]);

    $return_value = proc_close($process);
}

$output 现在包含了at写入 stderr 的内容(实际上应该写入 stdout,因为这不是一个错误),$return_value 包含成功的返回值 0


@Jack 你具体是什么意思? - Bojangles
我的意思是 echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes 2>&1", $result); - Ja͢ck
@Jack 它完美地运行了。你早点发帖不就能省下我几个小时的挣扎了吗?;) - Bojangles
5
哦不!抱歉,我必须在SO和加勒比海盗之间做出选择;-) - Ja͢ck

8

这里是使用proc_open的更复杂的解决方案。我写这个答案是因为在我的情况下,'2>&1'的解决方法不起作用。

function runCommand($bin, $command = '', $force = true)
{
    $stream = null;
    $bin .= $force ? ' 2>&1' : '';

    $descriptorSpec = array
    (
        0 => array('pipe', 'r'),
        1 => array('pipe', 'w')
    );

    $process = proc_open($bin, $descriptorSpec, $pipes);

    if (is_resource($process))
    {
        fwrite($pipes[0], $command);
        fclose($pipes[0]);

        $stream = stream_get_contents($pipes[1]);
        fclose($pipes[1]);

        proc_close($process);
    }

    return $stream;
}

使用示例:

// getting the mime type of a supplied file
echo runCommand('file -bi ' . escapeshellarg($file));

另一个使用命令参数的示例:

// executing php code on the fly
echo runCommand('/usr/bin/php', '<?php echo "hello world!"; ?>');

另一个使用force参数的示例(这对于在执行过程中更改输出的命令非常有用):

// converting an mp3 to a wav file
echo runCommand('lame --decode ' . escapeshellarg($source) . ' ' . escapeshellarg($dest), '', true);

我希望这可以帮到您,:-)。

谢谢你展示这个糟糕的例子!我现在正在做完全相同的事情。 - jeroen

1

尝试创建一个最小的(非)工作示例。将所有内容分解,并一次只测试一件事。

这是您的bash中的一个错误:

hpek@melda:~/temp$ echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes
at: pluralization is wrong
job 22 at Sun Jun 10 14:48:00 2012
hpek@melda:~/temp$ 

请使用 minute 代替 minutes

我从 at 得到的输出将通过邮件发送给我!


1
通过邮局还是联邦快递? - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接