Ответить на вопросы, заданные программой, запущенной из командной строки в Stack Overflow

Я хочу знать, как я могу взаимодействовать с программой, которую я запускаю в сценарии PHP командной строки. Сценарий таков:

  1. Начните выполнять программу.
  2. Читайте вывод до тех пор, пока не будет задан вопрос (думаю, читая STDOUT).
  3. Введите ответ и нажмите Enter (я думаю, написав в STDIN). Пользователь не должен вводить это, сценарий уже знает, что ответить, читая и интерпретируя выходные данные из шага 2.
  4. Снова прочитайте вывод, пока не будет задан новый вопрос.
  5. Снова введите ответ и нажмите Enter. Опять же, скрипт знает все, пользовательский ввод не должен происходить.
  6. Этот сценарий вопросов / ответов повторяется x раз, пока программа не будет завершена.

Как я могу написать скрипт PHP, который делает это? Я думаю, что я, вероятно, хочу использовать proc_open() но я не могу понять, как. Я думаю, что это будет что-то вроде этого, но это, конечно, не работает:

$descriptorspec = array(
0 => array('pipe', 'r'),  //STDIN
1 => array('pipe', 'w'),  //STDOUT
2 => array('pipe', 'r'),  //STDERR
);
$process = proc_open('mycommand', $descriptorspec, $pipes, null, null);
if (is_resource($process)) {
// Get output until first question is asked
while ($buffer = fgets($pipes[1])) {
echo $buffer;
}
if (strpos($buffer, 'STEP 1:') !== false) {
fwrite($pipes[0], "My first answer\n");  //enter the answer
} else {
die('Unexpected last line before question');
}

// Get output until second question is asked
while ($buffer = fgets($pipes[1])) {
echo $buffer;
}
if (strpos($buffer, 'STEP 2:') !== false) {
fwrite($pipes[0], "My second answer\n");  //enter the answer
} else {
die('Unexpected last line before question');
}

// ...and so we continue...
} else {
echo 'Not a resource';
}

ОБНОВИТЬ: Я понял, что программа выводит вопросы в STDERR (потому что записывает STDOUT в файл).

6

Решение

Надеемся, что следующего примера, основанного на приведенном выше коде, будет достаточно для начала работы.

Я протестировал его на Linux, хотя вы не указали, какую операционную систему вы использовали,
так что ваш пробег может отличаться, если вы работаете с чем-то другим.

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

Спасибо Крису и ignomueller.net«s
ответы на Обманите приложение, заставив его думать, что его стандартный ввод интерактивный, а не труба
мы можем запутать команду в том, что она говорит с терминалом, передав
это как аргумент script, который генерирует машинописный текст вывода команды.
Сохраняя машинописный текст в / dev / null, сбрасывая вывод после каждой записи (-f) а также
исключая стартовые и готовые сообщения (-q) это означает, что мы можем читать подсказки, как они
выводятся.

Вы указали STDOUT из команды должен быть отправлен в файл, тогда как
вопросы на STDERR. Это дополнительное осложнение, потому что
Кажется, что использованная логика не работает для чтения из STDERR. Однако, если вы перенаправите
STDOUT к файлу внутри -c параметр для script, а затем перенаправить scriptсобственный
STDERR для STDOUT, кажется, все работает нормально.

$descriptorspec = array(
0 => array('pipe', 'r'),  //STDIN
1 => array('pipe', 'w'),  //STDOUT
2 => array('pipe', 'r'),  //STDERR
);
// Avoid buffering by passing the command through "script"$process = proc_open(
'script -qfc "mycommand >mycommand.out" /dev/null 2>&1',
$descriptorspec, $pipes, null, null);
if (is_resource($process)) {
$buffer = "";
// Read from the command's STDOUT until it's closed.
while (!feof($pipes[1])) {
$input = fread($pipes[1], 8192);
$buffer .= $input;

// Output what we've read for debugging. You'd want to add some logic here
// instead to handle the input and work out the answers to the questions.
echo $input;

// Answer the questions when appropriate.
// This won't work if your output ever includes "STEP 1:" other than when
// prompting for a question. You might need some more robust logic here.
if (preg_match("/\nSTEP 1:$/", $buffer)) {
fwrite($pipes[0], "My first answer\n");
} elseif (preg_match("/\nSTEP 2:$/", $buffer)) {
fwrite($pipes[0], "My second answer\n");
}
}
proc_close($process);
} else {
echo 'Not a resource';
}

Я написал код таким образом, потому что вы указали, что ответы на запросы требуют «чтения и интерпретации вывода». Если все ответы были известны до запуска программы, решение будет намного проще. В этом случае вы можете просто вывести все ответы сразу, прежде чем начать читать ответ. Команде не важно, чтобы вы не ждали приглашения, прежде чем вводить данные. Вам может понадобиться позвонить stream_set_blocking($pipes[0], false); сначала в этом случае я не уверен на 100%.

0

Другие решения

Вы, безусловно, на правильном пути.

Лучше всего начать с вопиющей проблемы в вашем коде:

while ($buffer = fgets($pipes[1])) {
echo $buffer;
}

Этот цикл никогда не завершится. В какой-то момент программа задаст вам вопрос, но ваш код все еще выполняет (блокирующий) вызов fgets.

Что касается того, как писать код, который работает правильно ….

Наиболее очевидное решение — не ждать ответа, прежде чем дать ответ. Это будет работать до тех пор, пока:

  1. Вам не нужно адаптировать свой ответ на основе предыдущего вывода программы

  2. Программа читает со своего стандартного ввода и не очищает буфер в любой момент

На самом деле, вам даже не нужен процесс контроля для этого:

program <input.txt >output.txt 2>errors.txt

Но если предположить, что 1 и / или 2 не применимы, и учитывая, что он уже перенаправляет свой стандартный вывод (что говорит о том, что в истории есть нечто большее, чем мы знаем),

...
if (is_resource($process)) {
while ($buffer=fgets($pipes[2]) { // returns false at EOF/program termination
if (strpos($buffer, 'STEP 1:') !== false) {
fwrite($pipes[0], "My first answer\n");
} else if (strpos($buffer, 'STEP 2:') !== false) {
fwrite($pipes[0], "My second answer\n");  //enter the answer
}
}
}

Внедрение проверок для вопросов вне последовательности и ветвления в цикле запрос / ответ оставлено читателю в качестве упражнения.

0

По вопросам рекламы [email protected]