You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用exec执行多步骤交互式Bash命令?

解决PHP调用交互式命令行脚本的阻塞问题

我有一个多步骤的交互式命令行脚本,执行时会依次提问,需要在PHP代码里调用它:

php command.php
> What is your name?
User Smith
> What is your age?
25
> You are old enough!

但用exec调用时程序会卡住,我想实现类似这样的链式调用效果:

<?php

$result = exec('php command.php')->next('User Smith')->next('25');

if ($result->response === 'You are old enough!') echo "Yahoo";
else  echo "Oh no!";

问题原因

exec函数只能单向读取进程的标准输出,无法向进程的标准输入写入内容,所以遇到交互式脚本需要输入时,进程会一直等待输入,导致PHP代码卡住。

解决方案:用proc_open实现双向通信

proc_open可以创建子进程并打开标准输入、输出、错误的管道,让我们能主动给脚本输入内容,同时读取输出。下面是封装好的实现,能满足你想要的链式调用需求:

<?php

class InteractiveCommand
{
    private $process;
    private $pipes;
    private $output = '';

    public static function exec($command)
    {
        $descriptorSpec = [
            0 => ['pipe', 'r'], // 标准输入,可写
            1 => ['pipe', 'w'], // 标准输出,可读
            2 => ['pipe', 'w']  // 标准错误,可读
        ];

        $process = proc_open($command, $descriptorSpec, $pipes);

        if (!is_resource($process)) {
            throw new Exception('无法启动进程');
        }

        // 非阻塞读取,避免一开始就卡住
        stream_set_blocking($pipes[1], false);
        stream_set_blocking($pipes[2], false);

        $instance = new self();
        $instance->process = $process;
        $instance->pipes = $pipes;

        return $instance;
    }

    public function next($input)
    {
        // 写入输入内容,加换行符模拟回车
        fwrite($this->pipes[0], $input . PHP_EOL);
        // 短时间等待输出生成,实际可根据输出内容判断时机
        usleep(50000);
        // 读取当前输出
        $this->output .= stream_get_contents($this->pipes[1]);
        $this->output .= stream_get_contents($this->pipes[2]);

        return $this;
    }

    public function getResponse()
    {
        // 关闭输入,让进程执行完毕
        fclose($this->pipes[0]);
        // 读取剩余输出
        $this->output .= stream_get_contents($this->pipes[1]);
        $this->output .= stream_get_contents($this->pipes[2]);
        // 关闭管道并等待进程结束
        fclose($this->pipes[1]);
        fclose($this->pipes[2]);
        proc_close($this->process);

        // 提取最后一行输出(可根据脚本输出格式调整)
        $lines = explode(PHP_EOL, trim($this->output));
        return end($lines);
    }
}

// 使用示例
$result = InteractiveCommand::exec('php command.php')
    ->next('User Smith')
    ->next('25');

$response = $result->getResponse();

if ($response === 'You are old enough!') {
    echo "Yahoo";
} else {
    echo "Oh no!";
}

代码说明

  1. proc_open配置:定义三个管道对应标准输入、输出、错误流,实现双向通信。
  2. 非阻塞流设置:避免读取输出时因进程未生成内容而卡住。
  3. 链式调用:exec启动进程并返回实例,next写入输入并读取输出,最后getResponse关闭资源并提取最终结果。
  4. 等待逻辑:usleep是简单的等待方式,实际场景可循环读取直到出现预期提示(比如> What is your age?),这样更可靠。

内容的提问来源于stack exchange,提问作者AidOnline01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 19:57:18