go - golang : `os.Stdin.Read` doesn't handle input consisting of a sole EOF?

标签 go stdin eof cat file-descriptor

我是writing猫,在接收第一个字节时超时。我有它工作,除了它不能处理echo -n:

❯ echo -n | time possiblycat 1000  # 1000 is the timeout in milliseconds                                                                                        
possiblycat 1000  0.00s user 0.00s system 0% cpu 1.008 total; max RSS 1864
cat本身与此无关。它会注意到EOF并立即退出:
❯ echo -n | time cat                                                                                                      
cat  0.00s user 0.00s system 71% cpu 0.003 total; max RSS 664
这是possiblycat的全部来源:
package main

import (
    "io"
    "io/ioutil"
    "os"
    "strconv"
    "time"
)

func main() {
    wait := 10
    if len(os.Args) >= 2 {
        waitDummy, err := strconv.Atoi(os.Args[1])
        if err != nil {
            panic(err)
        }
        wait = waitDummy
    }

    b := make(chan byte, 1)
    go scan(b)

    select {
    case res := <-b:
        inBytes, err := ioutil.ReadAll(os.Stdin)
        if err != nil {
            panic(err)
        }
        stdin := append([]byte{res}, inBytes...)
        _, err2 := os.Stdout.Write(stdin)
        if err2 != nil {
            panic(err2)
        }
    case <-time.After(time.Duration(wait) * time.Millisecond):
        os.Exit(1)
    }
}

func scan(out chan byte) {
    var b []byte = make([]byte, 1)
    _, err := os.Stdin.Read(b)
    if err == io.EOF {
        return
    } else if err != nil {
        panic(err)
    }
    out <- b[0]
}

有关的:
  • Does echo -n | … send an EOF to the pipe?
  • 最佳答案

    os.Stdin.Read返回EOF时,您退出在其自己的goroutine中运行的scan函数。
    但是,并没有采取任何措施告诉主goroutine所有输入均已处理。它正在等待b channel 上的数据或超时。由于b上没有数据,因此达到了超时。
    为了正确处理此问题,err == io.EOF情况应向主goroutine发出信号,表明没有其他工作要做。一种常见的模式(但肯定不是唯一的一种)是具有一个done channel ,该 channel 指示所有工作已完成。

      done := make(chan bool, 1)
      go scan(b, done)
    
      select {
      case res := <-b:
        ...
      case <-done:
        os.Exit(1)
      case <-time.After(time.Duration(wait) * time.Millisecond):
        os.Exit(1)
      }
    }
    
    func scan(out chan byte, done chan bool) {
      var b []byte = make([]byte, 1)
      _, err := os.Stdin.Read(b)
      if err == io.EOF {
        fmt.Println("got EOF, exiting")
        done <- true
        return
      } else if err != nil {
      ...
    }
    
    另一种(甚至更简单的)替代方法是在完成后简单地关闭数据 channel :
    func scan(out chan byte) {
      var b []byte = make([]byte, 1)
      _, err := os.Stdin.Read(b)
      if err == io.EOF {
        fmt.Println("got EOF, exiting")
        close(out)
        return
      } else if err != nil {
        panic(err)
      }
      out <- b[0]
    }
    

    关于go - golang : `os.Stdin.Read` doesn't handle input consisting of a sole EOF?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63981760/

    相关文章:

    email - 如何使用 Outlook 的 SMTP 服务器发送电子邮件?

    amazon-web-services - 用 Go 插入 firehose

    perl - 在 perl 中,如何从 STDIN 读取直到我们找到一个空格或换行符?

    shell - fzf,stdin出现问题,并从rust :调用程序

    c - Stdin + 字典文本替换工具 -- 调试

    c - 使用 fscanf 读取文件时出错

    go - 使用 golang 代码部署 kubernetes Pod

    http - 上下文不超时

    c - 获取文本而不是 EOF

    c - 在 while 循环中使用 scanf 的问题