Golang中预检查os.Stdin流是否有数据,避免读取阻塞
解决Golang中预检查os.Stdin是否有数据再读取的问题
你的问题核心是要非阻塞式检查stdin是否已有数据,避免后续读取操作阻塞。原来的bufio.Reader.Peek()会阻塞直到有输入或EOF,不符合你"无数据直接报错"的需求。
解决方案:用syscall.Select做非阻塞可读检测
利用系统调用select可以在不阻塞的情况下检测文件描述符是否可读。结合你的需求(数据量<10MB),可以按以下方式修改代码:
import ( "errors" "io" "os" "syscall" "github.com/spf13/cobra" ) func ReadFromStdinIfAvailable(cmd *cobra.Command, args []string) ([]byte, error) { if len(args) == 0 { // 准备select的读集合 fd := os.Stdin.Fd() readSet := &syscall.FdSet{} readSet.Set(int(fd)) timeout := syscall.Timeval{Sec: 0, Usec: 0} // 立即超时,非阻塞检查 // 调用select检测是否有可读数据 _, err := syscall.Select(int(fd)+1, readSet, nil, nil, &timeout) if err != nil { return nil, err } // 检查stdin是否在可读集合中 if !readSet.IsSet(int(fd)) { cmd.Println(userstrings.NoFilenameProvidedErrorString) return nil, errors.New(userstrings.NoFilenameProvidedErrorString) } // 数据量小,直接一次性读取所有内容 data, err := io.ReadAll(os.Stdin) if err != nil { return nil, err } return data, nil } return nil, errors.New(userstrings.NoStdInProvidedErrorString) }
关键说明:
syscall.Select的超时设为0,会立即返回结果,不会阻塞等待输入。- 如果
select返回后stdin不在可读集合中,说明当前没有数据,直接返回错误。 - 因为数据量小于10MB,用
io.ReadAll一次性读取比bufio.Scanner更简洁高效,避免循环拼接的麻烦。 - 注意:这个方案基于类Unix系统(Linux/macOS),Windows系统需要改用
golang.org/x/sys/windows中的对应API,核心逻辑一致。
为什么原来的Peek不行?
bufio.Reader.Peek(n)会尝试从输入流中读取n个字节但不移动指针,如果当前流中没有足够数据,它会阻塞等待直到有数据到来或EOF,这就是你遇到阻塞的原因。而select的非阻塞检测能直接判断当前是否有可读数据,完全符合你的需求。
内容的提问来源于stack exchange,提问作者aronchick
相关产品推荐
相关产品推荐

