跳转到主要内容

简介

Laravel 的 Process Facade 是对 Symfony Process 组件的一层轻量封装。 它为在 Laravel 应用中调用 shell 命令、处理执行结果提供了简洁的 API。

启动进程

Process::run() 用于同步执行命令并获取结果。
use Illuminate\Support\Facades\Process;

$result = Process::run('ls -la');

return $result->output();
ProcessResult 实例提供了一系列检查结果的方法。
$result = Process::run('ls -la');

$result->command();       // 执行的命令
$result->successful();    // 是否成功(exit code 为 0)
$result->failed();        // 是否失败(exit code 非 0)
$result->output();        // 标准输出(stdout)
$result->errorOutput();   // 标准错误输出(stderr)
$result->exitCode();      // 退出码

失败时抛出异常

要在退出码大于 0 时抛出 Illuminate\Process\Exceptions\ProcessFailedException,可以使用 throw()throwIf()
$result = Process::run('ls -la')->throw();

$result = Process::run('ls -la')->throwIf($condition);

进程选项

工作目录

path() 指定工作目录,省略时会使用当前 PHP 脚本的工作目录。
$result = Process::path(__DIR__)->run('ls -la');

标准输入

input() 可以将数据写入进程的标准输入。
$result = Process::input('Hello World')->run('cat');

超时

默认超时是 60 秒,可以通过 timeout() 修改。超时后进程会被中断并抛出 ProcessTimedOutException
$result = Process::timeout(120)->run('bash import.sh');
也可以使用 CarbonInterval 相关辅助函数。
use function Illuminate\Support\minutes;

$result = Process::timeout(minutes(2))->run('bash import.sh');
若要关闭超时,可以使用 forever()
$result = Process::forever()->run('bash import.sh');
还可以设置空闲超时(一段时间没有输出即视为超时)。
$result = Process::timeout(60)->idleTimeout(30)->run('bash import.sh');

环境变量

env() 可以新增或覆盖环境变量。系统环境变量会自动继承。
$result = Process::forever()
    ->env(['IMPORT_PATH' => __DIR__])
    ->run('bash import.sh');
若要排除继承的变量,可以将其设为 false
$result = Process::forever()
    ->env(['LOAD_PATH' => false])
    ->run('bash import.sh');

关闭输出

如果不需要大量输出,可以用 quietly() 节省内存。
$result = Process::quietly()->run('bash import.sh');

实时输出

run() 的第二个参数传入闭包,即可实时接收输出。
$result = Process::run('ls -la', function (string $type, string $output) {
    echo $output;
});

管道

使用 Process::pipe() 可以把一条命令的输出作为下一条命令的输入。
use Illuminate\Process\Pipe;
use Illuminate\Support\Facades\Process;

$result = Process::pipe(function (Pipe $pipe) {
    $pipe->command('cat example.txt');
    $pipe->command('grep -i "laravel"');
});

if ($result->successful()) {
    // ...
}
也可以传入命令字符串数组。
$result = Process::pipe([
    'cat example.txt',
    'grep -i "laravel"',
]);
给每个进程用 as() 打上 key,就能在输出闭包中判断输出来自哪个进程。
$result = Process::pipe(function (Pipe $pipe) {
    $pipe->as('first')->command('cat example.txt');
    $pipe->as('second')->command('grep -i "laravel"');
}, function (string $type, string $output, string $key) {
    // $key 为 'first' 或 'second'
});

异步进程

Process::start() 可以异步启动进程。进程运行期间,应用可以继续处理其他工作。
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    // 其他处理
}

$result = $process->wait();

进程 ID 与信号

id() 用来获取运行中进程的 OS 进程 ID。
$process = Process::start('bash import.sh');

return $process->id();
signal() 可以向进程发送信号。
$process->signal(SIGUSR2);

异步进程的输出

运行过程中通过 latestOutput()latestErrorOutput() 可以获取自上次取用以来的增量输出。
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    echo $process->latestOutput();
    echo $process->latestErrorOutput();

    sleep(1);
}
要等某段输出出现再继续,可以使用 waitUntil()
$process = Process::start('bash import.sh');

$process->waitUntil(function (string $type, string $output) {
    return $output === 'Ready...';
});

异步进程的超时检查

在循环中调用 ensureNotTimedOut(),如果已经超时就会抛出异常。
$process = Process::timeout(120)->start('bash import.sh');

while ($process->running()) {
    $process->ensureNotTimedOut();

    sleep(1);
}

并发进程

使用 Process::pool() 可以并发执行多个进程。
use Illuminate\Process\Pool;
use Illuminate\Support\Facades\Process;

$pool = Process::pool(function (Pool $pool) {
    $pool->path(__DIR__)->command('bash import-1.sh');
    $pool->path(__DIR__)->command('bash import-2.sh');
    $pool->path(__DIR__)->command('bash import-3.sh');
})->start(function (string $type, string $output, int $key) {
    // ...
});

while ($pool->running()->isNotEmpty()) {
    // ...
}

$results = $pool->wait();
使用 concurrently() 可以一行完成“启动池并等待结果”。
[$first, $second, $third] = Process::concurrently(function (Pool $pool) {
    $pool->path(__DIR__)->command('ls -la');
    $pool->path(app_path())->command('ls -la');
    $pool->path(storage_path())->command('ls -la');
});

echo $first->output();

给进程命名

通过 as() 给每个进程加上字符串 key,可以更清晰地取得结果。
$pool = Process::pool(function (Pool $pool) {
    $pool->as('first')->command('bash import-1.sh');
    $pool->as('second')->command('bash import-2.sh');
    $pool->as('third')->command('bash import-3.sh');
})->start(function (string $type, string $output, string $key) {
    // ...
});

$results = $pool->wait();

return $results['first']->output();
也可以向整个池发送信号。
$pool->signal(SIGUSR2);

测试

伪造进程

Process::fake() 可以在不真正执行 shell 命令的情况下测试。
use Illuminate\Support\Facades\Process;

Process::fake();

$response = $this->get('/import');

Process::assertRan('bash import.sh');
也可以指定输出和退出码。
Process::fake([
    '*' => Process::result(
        output: 'Test output',
        errorOutput: 'Test error output',
        exitCode: 1,
    ),
]);

伪造特定命令

可以以通配符或命令字符串作为 key 分别配置。
Process::fake([
    'cat *'    => Process::result(output: 'file contents'),
    'ls -la'   => Process::result(output: 'file listing'),
]);

序列化的假响应

同一条命令被多次调用时,可以按顺序指定返回结果。
Process::fake([
    'ls *' => [
        Process::result('first time'),
        Process::result('second time'),
    ],
]);

断言

方法说明
Process::assertRan('command')断言命令被执行
Process::assertNotRan('command')断言命令未被执行
Process::assertRan(fn)通过闭包做更细致的校验
Process::assertRanTimes('command', 3)断言命令被执行了指定次数

阻止意外进程

调用 Process::preventStrayProcesses() 后,如果执行了未 fake 的命令,就会抛出异常。
Process::preventStrayProcesses();

Process::fake([
    'ls *' => Process::result('file listing'),
]);

// 异常:'bash import.sh' 没有被 fake
Process::run('bash import.sh');

相关页面

Artisan 控制台

创建自定义命令并从中调用进程

队列

与异步处理的对比
最后修改于 2026年7月13日