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

Go语言循环读取STDIN并阻塞:父子进程管道交互异常求助

问题分析与解决方案

你的核心问题是子进程在首次读取STDIN后,后续读取持续返回EOF导致无限循环,结合代码和场景来看,主要原因有两点:一是输入读取方式不匹配行读取需求,二是遇到EOF时未正确终止循环,同时父进程的管道写端处理也可能存在问题。

问题拆解

  1. fmt.Scan(&line)的局限性:该方法会跳过前导空白,且仅读取到下一个空白字符(空格、换行等)为止,无法完整读取整行输入,尤其不适合父进程按行发送数据的场景。
  2. EOF处理逻辑缺失:当父进程关闭管道写端后,子进程的STDIN会持续返回EOF,但你的代码仅打印错误却不终止循环,导致无限重复读取EOF。
  3. 父进程管道维护问题:如果父进程写完一次数据就关闭管道写端,子进程的STDIN会直接进入EOF状态,无法接收后续输入。

修复后的子进程代码

推荐使用bufio.Scanner实现可靠的行读取和阻塞等待,它会自动处理阻塞和EOF逻辑:

package main

import (
    "bufio"
    "log"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    // 循环阻塞读取STDIN,直到EOF或收到停止命令
    for scanner.Scan() {
        line := scanner.Text()
        if line == "stop" {
            break
        }
        // 这里添加你的业务逻辑,比如处理输入并输出到STDOUT
        log.Printf("收到输入: %s", line)
    }

    // 检查扫描过程中的异常错误(EOF不属于错误)
    if err := scanner.Err(); err != nil {
        log.Fatalf("读取STDIN失败: %v", err)
    }
    log.Println("程序正常退出")
}

如果你更倾向于使用bufio.Reader,可以这样实现:

package main

import (
    "bufio"
    "io"
    "log"
    "os"
    "strings"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                // 父进程关闭管道写端,正常退出循环
                log.Println("STDIN已关闭,退出程序")
                break
            }
            // 其他读取错误直接终止
            log.Fatalf("读取失败: %v", err)
        }
        // 去掉末尾的换行符
        line = strings.TrimSuffix(line, "\n")
        if line == "stop" {
            break
        }
        log.Printf("收到输入: %s", line)
    }
}

父进程注意事项

父进程需要保持管道写端打开,直到需要停止子进程时再发送stop命令或关闭写端,示例代码(Go语言父进程):

package main

import (
    "io"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("./your-child-process")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }

    // 启动子进程
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }

    // 向子进程发送多行数据
    _, err = io.WriteString(stdin, "hello\n")
    if err != nil {
        log.Fatal(err)
    }
    _, err = io.WriteString(stdin, "world\n")
    if err != nil {
        log.Fatal(err)
    }
    // 发送停止命令
    _, err = io.WriteString(stdin, "stop\n")
    if err != nil {
        log.Fatal(err)
    }
    stdin.Close() // 关闭写端,子进程处理完后退出

    // 读取子进程输出
    output, err := io.ReadAll(stdout)
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("子进程输出: %s", output)

    // 等待子进程退出
    if err := cmd.Wait(); err != nil {
        log.Fatal(err)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:30:43