Вывод оболочки не полностью извлекается из PHP! - PullRequest
4 голосов
/ 09 февраля 2009

У меня есть скрипт PHP, который выполняет команду оболочки:

$handle = popen('python last', 'r');
$read = fread($handle, 4096);
print_r($read);
pclose($handle);

Я повторяю вывод вывода оболочки. Когда я запускаю это в команде, я получаю что-то вроде этого:

[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, чтобы он создавал новую строку. Но это имело значение! (

Этот вопрос связан с этим .

Спасибо за любую помощь.

Обновление

Необходим для перенаправления вывода "2> & 1". Арулу повезло: P, так как я пропустил крайний срок, чтобы выбрать один верный ответ, который принадлежал Пакс!

Ответы [ 9 ]

19 голосов
/ 09 февраля 2009

Вы читаете только первые 4096 байт из канала, вам нужно поместить fread / print _r в цикл и проверить конец файла, используя функцию feof.

$handle = popen('python last', 'r');

while(!feof($handle))
{
    print_r(fread($handle, 4096));
} 

pclose($handle);
13 голосов
/ 16 февраля 2009

Первый шаг - увидеть, куда идет вывод. Первое, что я хотел бы сделать, это выбрать файл немного меньшего размера, чтобы вы не ожидали около семи минут для каждого теста.

Шаг 1 / Посмотрите, где что пишется в оболочке. Выполните команду python last >/tmp/stdout 2>/tmp/stderr, затем посмотрите на эти два файла. В идеале все будет записано на стандартный вывод, но это может быть не так. Это дает вам базовое поведение сценария.

Шаг 2 / Сделайте то же самое при запуске из PHP с помощью $handle = popen('python last >/tmp/stdout 2>/tmp/stderr', 'r');. В этом случае ваш PHP-скрипт, вероятно, ничего не вернет, но файлы все равно должны быть заполнены Это отразит любое измененное поведение при работе в нетерминальной среде.

Если часть вывода идет в stderr, то решение должно быть таким простым: $handle = popen('python last 2>&1', 'r');

Кроме того, документация по PHP-состояниям (мое выделение):

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 <strong>fgets()</strong>, <strong>fgetss()</strong>, and <strong>fwrite()</strong>.

Так что я не уверен, что вы даже должны использовать fread(), хотя это показано в одном из примеров. Тем не менее, я думаю, что линейный ввод больше соответствует тому, чего вы пытаетесь достичь.

Независимо от всего этого, вы должны прочитать вывод в цикле, чтобы убедиться, что вы можете получить вывод, когда он больше 4K, что-то вроде:

$handle = popen ('python last 2>&1', 'r');
if ($handle) {
    while(! feof ($handle)) {
        $read = fgets ($handle);
        echo $read;
    } 
    pclose ($handle);
}

Еще одна вещь, на которую следует обратить внимание: если вы выводите данные в браузер и это занимает слишком много времени, сам браузер может отключиться, поскольку считает, что соединение на стороне сервера исчезло. Если вы обнаружите, что небольшой файл работает, а ваш файл 10M / 1minute не работает, это может иметь место. Вы можете попробовать flush(), но не все браузеры выполнят это.

5 голосов
/ 16 февраля 2009

Гораздо проще сделать это:

$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";
?>

Файл захвата .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, используя обратные пометки, самый простой способ.

4 голосов
/ 17 февраля 2009

Я не удивлюсь, обнаружив, что отчет о ходе работы опускается, когда вывод не идет в tty. То есть PHP захватывает все, что отправлено, но отчет о ходе работы не отправляется.

Существует достаточный прецедент для команд, которые ведут себя по-разному в зависимости от того, куда выводятся данные - начиная с старой доброй команды ls.

Конечно, если вы написали скрипт Python, который вы запускаете, то вероятность возникновения проблемы гораздо меньше.

Как вы можете проверить, верна ли эта гипотеза? Ну, вы могли бы начать с запуска сценария в командной строке, при этом стандартный вывод переходит в один файл, а стандартная ошибка - в другой. Если вы видите информацию о прогрессе в одном из этих файлов, вы знаете намного больше о том, что происходит. Если вы все еще видите информацию о прогрессе на экране, то сценарий, вероятно, выводит информацию о прогрессе на /dev/tty, но когда PHP ее запускает, /dev/tty для процесса не происходит. Если вы вообще не видите информацию о ходе выполнения (на экране или в файле), то моя первоначальная гипотеза, вероятно, подтверждена.

3 голосов
/ 18 февраля 2009

Попробуйте запустить stream_get_contents () для $ handle. Это лучший способ работы с ресурсами, когда вы не знаете точный размер того, что пытаетесь получить.

2 голосов
/ 17 февраля 2009

Если вы просто пытаетесь показать ход выполнения команды python внутри окна терминала, то я бы порекомендовал следующее:

<?php
system("python last > `tty`");

Вам не нужно будет захватывать вывод, и пользователь может даже Ctrl + C загрузить без прерывания PHP-сценария.

2 голосов
/ 09 февраля 2009

Что вы видите с

python last | less

Возможно, требуемый вывод генерируется на STDERR. Тогда вы должны начать это так:

$handle = popen('python last 2>&1', 'r');

2>&1 направляет STDERR в STDOUT, который вы захватываете с помощью popen .

2 голосов
/ 09 февраля 2009

Не могли бы вы читать в цикле while вместо использования fread ()?

while( !feof($handle) )
    echo fgets($handle);

Возможно, вам также придется выполнить сброс ().

1 голос
/ 19 февраля 2009

Вы пропустили флеш-колл. (В вашем приложении на Python и, возможно, в вашем php-приложении)

Это потому, что когда вы используете стандартный поток stdin / stdout в интерактивном режиме (из cmdline), они находятся в режиме буферизации строки (в коротких системных сбросах на каждой новой строке), но когда вы вызываете его изнутри вашей программы, потоки полностью буферизованный режим (не выводится, пока системный буфер не заполнится).

Подробнее об этом здесь Буферизация в стандартных потоках

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...