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

测试CLI交互式提示:如何让scanner.Scan等待后续写入?

如何让bufio.Scanner等待后续写入以测试CLI提示?

我需要编写测试验证CLI提示,模拟用户输入并响应程序输出,但遇到了scanner.Scan无法等待后续写入的问题,以下是我的尝试及问题:

尝试1:使用bytes.Buffer实现

b := &bytes.Buffer{}
fmt.Fprint(b, "0")
go func() {
    time.Sleep(1 * time.Second)
    for i := 1; i < 4; i++ {
        fmt.Fprint(b, i)
        time.Sleep(1 * time.Second)
    }
}()

scanner := bufio.NewScanner(b)
for scanner.Scan() {
    log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
    log.Println("problem while scanning:", err)
}
  • 预期结果:输出0123
  • 实际结果:仅输出0

尝试2:使用io.Pipe实现

r, w := io.Pipe()
fmt.Fprint(w, "0")
go func() {
    time.Sleep(1 * time.Second)
    for i := 1; i < 4; i++ {
        fmt.Fprint(w, i)
        time.Sleep(1 * time.Second)
    }
}()

scanner := bufio.NewScanner(r)
for scanner.Scan() {
    log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
    log.Println("problem while scanning:", err)
}
  • 结果:触发fatal error: all goroutines are asleep - deadlock!(致命错误:所有goroutine都处于休眠状态 - 死锁!)

问题原因及解决方案

问题分析

  1. bytes.Buffer版本:
    bufio.Scanner默认按行扫描(分隔符为\n),写入的内容没有换行符,且bytes.Buffer是内存缓冲区,scanner读完当前所有内容后就会返回false退出循环,不会主动等待后续写入。

  2. io.Pipe版本:
    io.Pipe的Write操作是阻塞的,直到对应Read操作读取数据;而scanner默认按行扫描,写入的内容无换行符,导致Read一直等待换行,goroutine中的Write也随之阻塞,最终所有goroutine卡住引发死锁。

修正方案

方案1:基于io.Pipe的行扫描(推荐,贴近真实CLI场景)

给每个写入内容添加换行符,写完后关闭写端,让scanner能正确识别每行数据,且不会无限阻塞:

r, w := io.Pipe()
go func() {
    defer w.Close() // 写完后关闭写端,触发scanner退出循环
    fmt.Fprintln(w, "0") // 写入带换行的内容
    time.Sleep(1 * time.Second)
    for i := 1; i < 4; i++ {
        fmt.Fprintln(w, i) // 每个输入都带换行
        time.Sleep(1 * time.Second)
    }
}()

scanner := bufio.NewScanner(r)
for scanner.Scan() {
    log.Print(scanner.Text())
}
if err := scanner.Err(); err != nil {
    log.Println("problem while scanning:", err)
}

此代码会依次输出0、1、2、3,符合预期逻辑。

方案2:基于bytes.Buffer的字节扫描

若需要读取单个字符而非行,可自定义scanner的分割函数为bufio.ScanBytes,并通过通道同步等待goroutine写入完成:

b := &bytes.Buffer{}
done := make(chan struct{})
go func() {
    defer close(done)
    fmt.Fprint(b, "0")
    time.Sleep(1 * time.Second)
    for i := 1; i < 4; i++ {
        fmt.Fprint(b, i)
        time.Sleep(1 * time.Second)
    }
}()

scanner := bufio.NewScanner(b)
scanner.Split(bufio.ScanBytes) // 按单个字节扫描
for {
    select {
    case <-done:
        // 读取goroutine写完后的剩余内容
        for scanner.Scan() {
            log.Print(scanner.Text())
        }
        goto exitLoop
    default:
        if scanner.Scan() {
            log.Print(scanner.Text())
        }
        time.Sleep(100 * time.Millisecond) // 避免空转占用CPU
    }
}
exitLoop:
if err := scanner.Err(); err != nil {
    log.Println("problem while scanning:", err)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 05:15:35