全栈应用服务器

  • 全栈应用服务器 > SDK 下载 > Go SDK 概览 > PTY 终端

    PTY 终端

    最近更新时间: 2026-03-05 19:04:50

    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=xtermLANG=C.UTF-8LC_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())
    }
    
    以上内容是否对您有帮助?