我有一个执行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
正如你可以看到底部位缺less这是我需要的位! 之前的问题是百分比正在同一行更新,但现在我已经改变了我的Python脚本,以便它创build一个新的行。 但是这有所作为! 🙁
这个问题与这个有关。
感谢您的任何帮助。
需要redirect输出“2>&1”。 阿鲁很幸运:因为我错过了最后期限select属于帕克斯的一个真正的答案!
您只读取管道中的前4,096个字节,您需要将fread
/ print
_r放在一个循环中,并使用feof
函数检查文件结束。
$handle = popen('python last', 'r'); while(!feof($handle)) { print_r(fread($handle, 4096)); } pclose($handle);
第一步是看看输出的地方。 我要做的第一件事就是选择一个稍微小一些的文件,这样你就不用等待每个测试七分钟。
步骤1 /查看在shell中写入的内容。 执行命令python last >/tmp/stdout 2>/tmp/stderr
然后查看这两个文件。 理想情况下,所有的东西都会被写入标准输出,但事实并非如此。 这为您提供了脚本的基本行为。
步骤2 /通过使用$handle = popen('python last >/tmp/stdout 2>/tmp/stderr', 'r');
从PHP运行时执行相同的操作$handle = popen('python last >/tmp/stdout 2>/tmp/stderr', 'r');
。 你的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()
但并不是所有的浏览器都会遵守这个。
这样做要容易得多:
$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);
或者使用proc_open() ,它比popen提供更多的控制,因为你可以指定要在哪里处理stdin,stdout和stderr。
或者你可以打开STDIN
或php://stdin
,然后管道到PHP:
? python last | php script.php
我会选择1,并使用反引号,最简单的方法。
当输出不打印到tty时,我不会惊讶地发现进度报告被省略了。 也就是说,PHP正在捕获发送的所有内容,但进度报告没有被发送。
有很多先例的命令行为有所不同,取决于输出的位置 – 从良好的旧ls
命令开始。
当然,如果你编写了你正在运行的Python脚本,那么这个问题就不太可能成为问题的原因。
你怎么能证实这个假设是否有效? 那么,你可以开始在命令行运行脚本,标准输出转到一个文件,标准错误转到另一个文件。 如果您在其中一个文件中看到进度信息,您就知道更多关于正在发生的事情。 如果你仍然在屏幕上看到进度信息,那么脚本可能会将进度信息回显到/dev/tty
,但是当PHP运行它时,进程没有/dev/tty
。 如果您根本没有看到进度信息(屏幕上或文件中),那么我原来的假设可能已经被证实。
尝试在$ handle上运行stream_get_contents()。 在不知道要检索的确切大小的情况下,使用资源是一种更好的方法。
你能读一个while循环,而不是使用fread()?
while( !feof($handle) ) echo fgets($handle);
你可能不得不flush()。
你看到了什么?
python last | less
?
也许你想要的输出是在STDERR上发射的。 那么你必须这样开始:
$handle = popen('python last 2>&1', 'r');
2>&1
将STDERR引导到STDOUT,您正在使用popen捕捉。
如果你只是试图在终端窗口中显示python命令的进度,那么我会推荐下面的代码:
<?php system("python last > `tty`");
那么您将不需要捕获输出,用户甚至可以在不中止PHP脚本的情况下Ctrl + C下载。
你错过了一个同花顺的电话。 (在你的Python应用程序,也可能是你的PHP应用程序)
这是因为,当你使用标准流stdin / stdout(从cmdline)交互时,它们处于行缓冲模式(在每个新行中刷新系统),但是当你在程序内部调用它时,流处于完全缓冲模式(直到系统缓冲区满了才输出)。
有关此处的更多信息,请参阅标准流缓冲