我有一个执行shell命令的PHP脚本:
$handle = popen('python last', 'r');
$read = fread($handle, 4096);
print_r($read);
pclose($handle);
我回显了shell输出的输出。当我在命令中运行它时,我得到这样的东西:
[root@localhost tester]# python last
[last] ZVZX-W3vo9I: Downloading video webpage
[last] ZVZX-W3vo9I: Extracting video information
[last] ZVZX-W3vo9I: URL: x
[download] Destination: here.flv
[download] 0.0% of 10.09M at ---b/s ETA --:--
[download] 0.0% of 10.09M at 22.24k/s ETA 07:44
[download] 0.0% of 10.09M at 66.52k/s ETA 02:35
[download] 0.1% of 10.09M at 154.49k/s ETA 01:06
[download] 0.1% of 10.09M at 162.45k/s ETA 01:03
但是,当我从PHP运行相同的命令时,我得到了这个输出:
[last] ZVZX-W3vo9I: Downloading video webpage
[last] ZVZX-W3vo9I: Extracting video information
[last] ZVZX-W3vo9I: URL: x
[download] Destination: here.flv
正如你所看到的那样,我需要的位是缺少的!之前的问题是百分比在同一行上更新,但现在我已经更改了我的Python脚本,以便创建一个新行。但这有所不同! :(
此问题与to this one相关。
感谢您的帮助。
需要重定向输出“2>& 1”。 Arul很幸运:P因为我错过了截止日期来选择属于Pax的真正答案!
答案 0 :(得分:19)
您只读取管道中的前4,096个字节,您需要将fread
/ print
_ r置于循环中并使用{{1}检查文件结尾功能。
feof
答案 1 :(得分:13)
第一步是查看输出的去向。我要做的第一件事就是选择一个稍小的文件,这样你就不会在每次测试中等待7分钟。
步骤1 /查看shell中的内容写入位置。执行命令python last >/tmp/stdout 2>/tmp/stderr
然后查看这两个文件。理想情况下,所有内容都将写入stdout,但情况可能并非如此。这为您提供了脚本的基线行为。
步骤2 /使用$handle = popen('python last >/tmp/stdout 2>/tmp/stderr', 'r');
从PHP运行时执行相同的操作。在这种情况下,您的PHP脚本可能不会返回任何内容,但仍应填充文件。这将捕获在非终端环境中运行时的任何更改行为。
如果某些输出转到stderr,那么解决方案应该像$handle = popen('python last 2>&1', 'r');
此外,PHP的doco指出(我的粗体):
Returns a file pointer identical to that returned by fopen(), except that it is unidirectional (may only be used for reading or writing) and must be closed with pclose(). This pointer may be used with fgets(), fgetss(), and fwrite().
所以我不确定你是否应该使用fread()
,尽管它在其中一个例子中显示。尽管如此,我认为基于行的输入更多地映射到您想要实现的目标。
无论如何,您应该在循环中读取输出,以确保在超过4K时可以获得输出,如:
$handle = popen ('python last 2>&1', 'r');
if ($handle) {
while(! feof ($handle)) {
$read = fgets ($handle);
echo $read;
}
pclose ($handle);
}
另外需要注意的是,如果您输出的是浏览器并且需要太长时间,浏览器本身可能会超时,因为它认为服务器端连接已经消失。如果您发现一个小文件正常工作且您的10M / 1分钟文件无法正常工作,则可能就是这种情况。您可以尝试flush()
,但并非所有浏览器都会尊重这一点。
答案 2 :(得分:5)
这样做要容易得多:
$output = `python last`;
var_dump($output);
'ticks'(`)将执行该行并捕获输出。这是一个测试示例:
文件test.php:
<?php
echo "PHP Output Test 1\n";
echo "PHP Output Test 2\n";
echo "PHP Output Test 3\n";
echo "PHP Output Test 4\n";
echo "PHP Output Test 5\n";
?>
文件capture.php:
<?php
$output = `php test.php`;
var_dump($output);
?>
php capture.php 的输出:
string(80) "Test PHP Script
Test PHP Script
Test PHP Script
Test PHP Script
Test PHP Script
"
然后,您可以根据换行符将输出拆分为数组:
$outputArray = explode('\n', $output);
OR 使用proc_open(),它提供了比 popen 更多的控制权,因为您可以指定要处理stdin,stdout和stderr的位置。< / p>
或你可以fopen STDIN
或php://stdin
然后管道到php:
? python last | php script.php
我会使用选项1并使用反引号,最简单的方法。
答案 3 :(得分:4)
当输出未达到tty时,我不会惊讶地发现进度报告被省略。也就是说,PHP正在捕获发送的所有内容,但是没有发送进度报告。
根据输出的位置,命令的行为有很多先例 - 从良好的旧ls
命令开始。
当然,如果您编写了正在运行的Python脚本,那么这就不太可能成为问题的原因。
如何验证此假设是否有效?好吧,你可以从命令行运行脚本开始,标准输出转到一个文件,标准错误转到另一个文件。如果您在其中一个文件中看到进度信息,那么您将了解更多有关正在发生的事情的信息。如果您仍然在屏幕上看到进度信息,那么脚本可能会将进度信息回显到/dev/tty
,但是当PHP运行它时,该进程没有/dev/tty
。如果您根本没有看到进度信息(在屏幕上或文件中),那么我原来的假设可能已经过验证。
答案 4 :(得分:3)
尝试在$ handle上运行stream_get_contents()。这是一种更好的方式来处理资源,而你不知道你想要检索的确切大小。
答案 5 :(得分:2)
你可以在while循环中读取而不是使用fread()吗?
while( !feof($handle) )
echo fgets($handle);
你也可能需要冲洗()。
答案 6 :(得分:2)
你对
有什么看法?python last | less
也许您想要的输出是在STDERR上发出的。然后你必须这样开始:
$handle = popen('python last 2>&1', 'r');
2>&1
将STDERR指向您正在使用popen捕获的STDOUT。
答案 7 :(得分:2)
如果您只是想在终端窗口中显示python命令的进度,那么我会推荐以下内容:
<?php
system("python last > `tty`");
您不需要捕获输出,用户甚至可以 Ctrl + C 下载而不会中止PHP脚本。
答案 8 :(得分:1)
你错过了一个同花顺的电话。 (在你的python应用程序中,可能还有你的php应用程序)
这是因为当您以交互方式使用标准流stdin / stdout时(来自cmdline),它们处于行缓冲模式(在每个新行上简短的系统刷新),但是当您从程序中调用它时,流是完全缓冲模式(在系统缓冲区已满之前不输出)。
有关此内容的更多信息buffering in standard streams