命令执行
通过 sb.Commands() 在沙箱内执行终端命令,就像在真实的 Linux 终端中一样。支持同步/异步执行、流式输出、后台进程管理。
同步执行命令
result, err := sb.Commands().Run(ctx, "echo hello world")
if err != nil {
log.Fatalf("执行命令失败: %v", err)
}
fmt.Printf("退出码: %d\n", result.ExitCode)
fmt.Printf("stdout: %s", result.Stdout)
fmt.Printf("stderr: %s", result.Stderr)
CommandResult 包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
ExitCode |
int |
进程退出码 |
Stdout |
string |
标准输出 |
Stderr |
string |
标准错误输出 |
Error |
string |
错误信息 |
命令选项
通过 CommandOption 自定义命令行为:
result, err := sb.Commands().Run(ctx, "echo $MY_VAR",
sandbox.WithEnvs(map[string]string{"MY_VAR": "hello"}),
sandbox.WithCwd("/tmp"),
sandbox.WithTimeout(5*time.Second),
)
| 选项 | 说明 |
|---|---|
WithEnvs(map[string]string) |
设置环境变量 |
WithCwd(string) |
设置工作目录 |
WithCommandUser(string) |
设置执行用户(默认 user) |
WithTag(string) |
设置进程标签,方便后续识别 |
WithOnStdout(func([]byte)) |
stdout 实时回调 |
WithOnStderr(func([]byte)) |
stderr 实时回调 |
WithTimeout(time.Duration) |
命令超时时间 |
流式输出
通过回调函数实时接收标准输出和错误输出,适用于长时间运行的命令。
result, err := sb.Commands().Run(ctx, "for i in 1 2 3; do echo line-$i; sleep 1; done",
sandbox.WithOnStdout(func(data []byte) {
fmt.Printf("[stdout] %s", string(data))
}),
sandbox.WithOnStderr(func(data []byte) {
fmt.Printf("[stderr] %s", string(data))
}),
)
后台命令
启动后台命令
使用 Start 启动不阻塞主程序的后台命令。
handle, err := sb.Commands().Start(ctx, "sleep 30", sandbox.WithTag("bg-task"))
if err != nil {
log.Fatalf("启动后台命令失败: %v", err)
}
// 等待 PID 分配
pid, err := handle.WaitPID(ctx)
if err != nil {
log.Fatalf("等待 PID 失败: %v", err)
}
fmt.Printf("后台命令 PID: %d\n", pid)
等待命令完成
handle, err := sb.Commands().Start(ctx, "echo done")
if err != nil {
log.Fatalf("启动命令失败: %v", err)
}
result, err := handle.Wait()
if err != nil {
log.Fatalf("等待命令失败: %v", err)
}
fmt.Printf("退出码: %d, stdout: %s", result.ExitCode, result.Stdout)
列出运行中的进程
processes, err := sb.Commands().List(ctx)
if err != nil {
log.Fatalf("列出进程失败: %v", err)
}
for _, p := range processes {
tag := "<none>"
if p.Tag != nil {
tag = *p.Tag
}
fmt.Printf("PID: %d, 命令: %s, 标签: %s\n", p.PID, p.Cmd, tag)
}
ProcessInfo 包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
PID |
uint32 |
进程 ID |
Tag |
*string |
进程标签 |
Cmd |
string |
命令 |
Args |
[]string |
命令参数 |
Envs |
map[string]string |
环境变量 |
Cwd |
*string |
工作目录 |
终止进程
if err := sb.Commands().Kill(ctx, handle.PID()); err != nil {
log.Fatalf("终止命令失败: %v", err)
}
也可以通过 CommandHandle 直接终止:
if err := handle.Kill(ctx); err != nil {
log.Fatalf("终止命令失败: %v", err)
}
发送标准输入
向运行中的进程发送数据到标准输入。
if err := sb.Commands().SendStdin(ctx, handle.PID(), []byte("input data\n")); err != nil {
log.Fatalf("发送 stdin 失败: %v", err)
}
连接到已有进程
连接到已经在运行的进程,获取其输出流。
handle, err := sb.Commands().Connect(ctx, pid)
if err != nil {
log.Fatalf("连接进程失败: %v", err)
}
result, err := handle.Wait()
CommandHandle 方法
| 方法 | 说明 |
|---|---|
PID() uint32 |
返回进程 ID |
Wait() (*CommandResult, error) |
等待命令完成并返回结果 |
Kill(ctx) error |
终止命令 |
WaitPID(ctx) (uint32, error) |
等待 PID 分配(用于后台命令启动后) |
完整示例
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/qiniu/go-sdk/v7/sandbox"
)
func main() {
c, err := sandbox.NewClient(&sandbox.Config{
APIKey: os.Getenv("QINIU_API_KEY"),
Endpoint: os.Getenv("QINIU_SANDBOX_API_URL"),
})
if err != nil {
log.Fatalf("创建客户端失败: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
timeout := int32(120)
sb, _, err := c.CreateAndWait(ctx, sandbox.CreateParams{
TemplateID: "base",
Timeout: &timeout,
}, sandbox.WithPollInterval(2*time.Second))
if err != nil {
log.Fatalf("创建沙箱失败: %v", err)
}
defer sb.Kill(context.Background())
// 同步执行
result, err := sb.Commands().Run(ctx, "echo hello world")
if err != nil {
log.Fatalf("执行命令失败: %v", err)
}
fmt.Printf("stdout: %s", result.Stdout)
// 带环境变量
result, err = sb.Commands().Run(ctx, "echo $MY_VAR",
sandbox.WithEnvs(map[string]string{"MY_VAR": "sandbox-value"}),
)
if err != nil {
log.Fatalf("执行命令失败: %v", err)
}
fmt.Printf("stdout: %s", result.Stdout)
// 指定工作目录
result, err = sb.Commands().Run(ctx, "pwd", sandbox.WithCwd("/tmp"))
if err != nil {
log.Fatalf("执行命令失败: %v", err)
}
fmt.Printf("stdout: %s", result.Stdout)
// 流式输出
_, err = sb.Commands().Run(ctx, "echo out-line && echo err-line >&2",
sandbox.WithOnStdout(func(data []byte) { fmt.Printf("[stdout] %s", string(data)) }),
sandbox.WithOnStderr(func(data []byte) { fmt.Printf("[stderr] %s", string(data)) }),
)
if err != nil {
log.Fatalf("流式命令失败: %v", err)
}
// 后台命令
handle, err := sb.Commands().Start(ctx, "sleep 10", sandbox.WithTag("bg"))
if err != nil {
log.Fatalf("启动后台命令失败: %v", err)
}
pid, err := handle.WaitPID(ctx)
if err != nil {
log.Fatalf("等待 PID 失败: %v", err)
}
fmt.Printf("后台命令 PID: %d\n", pid)
// 列出进程
processes, err := sb.Commands().List(ctx)
if err != nil {
log.Fatalf("列出进程失败: %v", err)
}
fmt.Printf("运行中的进程: %d 个\n", len(processes))
// 终止后台命令
sb.Commands().Kill(ctx, handle.PID())
}
文档反馈
(如有产品使用问题,请 提交工单)