PTY 终端
通过 sb.Pty() 创建和管理伪终端(PTY)会话,支持交互式命令操作。
创建 PTY 会话
var output []byte
ptyHandle, err := sb.Pty().Create(ctx, sandbox.PtySize{Cols: 80, Rows: 24},
sandbox.WithOnStdout(func(data []byte) {
output = append(output, data...)
}),
)
if err != nil {
log.Fatalf("创建 PTY 失败: %v", err)
}
// 等待 PID 分配
pid, err := ptyHandle.WaitPID(ctx)
if err != nil {
log.Fatalf("等待 PID 失败: %v", err)
}
fmt.Printf("PTY PID: %d\n", pid)
Create 默认启动 /bin/bash -i -l,并自动设置 TERM=xterm、LANG=C.UTF-8、LC_ALL=C.UTF-8 环境变量。
PTY 选项
Create 支持与命令执行相同的 CommandOption:
| 选项 | 说明 |
|---|---|
WithOnStdout(func([]byte)) |
接收 PTY 输出(回退方式) |
WithOnPtyData(func([]byte)) |
接收 PTY 输出(优先使用) |
WithEnvs(map[string]string) |
自定义环境变量(会与默认 PTY 环境变量合并) |
WithCwd(string) |
设置工作目录 |
WithTag(string) |
设置进程标签 |
注意: WithOnPtyData 优先级高于 WithOnStdout。如果两者都设置,PTY 输出只会通过 WithOnPtyData 回调发送。
发送输入
向 PTY 会话发送键盘输入。
if err := sb.Pty().SendInput(ctx, ptyHandle.PID(), []byte("echo hello\n")); err != nil {
log.Fatalf("发送输入失败: %v", err)
}
调整终端大小
if err := sb.Pty().Resize(ctx, ptyHandle.PID(), sandbox.PtySize{Cols: 120, Rows: 40}); err != nil {
log.Fatalf("调整大小失败: %v", err)
}
连接到已有 PTY
连接到已经运行的 PTY 会话。
handle, err := sb.Pty().Connect(ctx, pid)
if err != nil {
log.Fatalf("连接 PTY 失败: %v", err)
}
终止 PTY
if err := sb.Pty().Kill(ctx, ptyHandle.PID()); err != nil {
log.Fatalf("终止 PTY 失败: %v", err)
}
PtySize
| 字段 | 类型 | 说明 |
|---|---|---|
Cols |
uint32 |
列数(字符宽度) |
Rows |
uint32 |
行数(字符高度) |
完整示例
package main
import (
"context"
"fmt"
"log"
"os"
"sync"
"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())
// 创建 PTY(使用 mutex 保护并发写入)
var mu sync.Mutex
var ptyOutput []byte
received := make(chan struct{}, 1)
ptyHandle, err := sb.Pty().Create(ctx, sandbox.PtySize{Cols: 80, Rows: 24},
sandbox.WithOnStdout(func(data []byte) {
mu.Lock()
ptyOutput = append(ptyOutput, data...)
mu.Unlock()
select {
case received <- struct{}{}:
default:
}
}),
)
if err != nil {
log.Fatalf("创建 PTY 失败: %v", err)
}
if _, err := ptyHandle.WaitPID(ctx); err != nil {
log.Fatalf("等待 PID 失败: %v", err)
}
fmt.Printf("PTY 已创建: PID=%d\n", ptyHandle.PID())
// 发送命令并等待输出
sb.Pty().SendInput(ctx, ptyHandle.PID(), []byte("echo pty-hello\n"))
// 等待收到输出(带超时保护)
select {
case <-received:
case <-time.After(3 * time.Second):
}
mu.Lock()
fmt.Printf("PTY 输出: %q\n", string(ptyOutput))
mu.Unlock()
// 调整终端大小
sb.Pty().Resize(ctx, ptyHandle.PID(), sandbox.PtySize{Cols: 120, Rows: 40})
fmt.Println("PTY 已调整为 120x40")
// 终止 PTY
sb.Pty().Kill(ctx, ptyHandle.PID())
fmt.Printf("PTY PID=%d 已终止\n", ptyHandle.PID())
}
文档反馈
(如有产品使用问题,请 提交工单)