回答在 PHP 命令行上运行的程序提出的问题
Posted
技术标签:
【中文标题】回答在 PHP 命令行上运行的程序提出的问题【英文标题】:Respond to questions asked by a program run on the command line in PHP 【发布时间】:2016-03-06 08:04:30 【问题描述】:我想知道如何与在命令行 php 脚本中运行的程序进行交互。场景是:
-
开始执行程序。
阅读输出,直到有人提出问题(我猜是通过阅读 STDOUT)。
输入答案并按 Enter(我猜是写到 STDIN)。用户不要输入这个,脚本已经通过阅读和解释步骤 2 的输出知道要回答什么。
再次阅读输出,直到提出新问题。
再次输入答案并按 Enter。同样,脚本知道这一切,不会发生用户输入。
此问答场景重复 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 写入文件)。
【问题讨论】:
看看这个:***.com/questions/2929629/… @jsxqf 您没有阅读我的问题。这与我与 PHP 交互无关。这是关于 PHP 与另一个程序交互(没有我的任何输入)。 您的外部程序是否在“STEP 1:”和“STEP 2:”之后以及预期响应之前立即输出换行符(“\n”)?另外,问题总是相同且顺序相同吗? 我认为套接字将是同样的好选择? 你在什么操作系统上运行代码? 【参考方案1】:你肯定是在正确的轨道上。
最好从代码中明显的问题开始:
while ($buffer = fgets($pipes[1]))
echo $buffer;
这个循环永远不会退出。有时程序会问你一个问题,但此时你的代码仍在执行(阻塞)fgets 调用。
至于如何编写能正常工作的代码....
最明显的解决方案是在提供答案之前不必等待提示。只要:
您无需根据程序前面的输出调整您的响应
程序正在从其标准输入读取数据,并且在任何时候都不会清除缓冲区
事实上,您甚至不需要对此进行控制:
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
对乱序问题的检查和请求/响应周期中的分支留给读者练习。
【讨论】:
【参考方案2】:希望以下基于您上面的代码的示例足以帮助您入门。
我已经在 Linux 上进行了测试,尽管您没有指定您使用的是哪个操作系统, 因此,如果您正在运行其他项目,您的里程可能会有所不同。
因为命令通过管道传送到另一个进程,所以输出将被缓冲。 这意味着我们不会收到提示,因为缓冲区永远等待 发送当前行前换行且提示后面不跟 换行。
感谢 Chris 和 ignomueller.net's
回复Trick an application into thinking its stdin is interactive, not a pipe
我们可以通过传递将命令混淆为认为它正在与终端交谈
它作为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% 确定。
【讨论】:
以上是关于回答在 PHP 命令行上运行的程序提出的问题的主要内容,如果未能解决你的问题,请参考以下文章