将文件用作 Stdin (Golang) 时在第二次提示时获取 EOF

Getting EOF on 2nd prompt when using a file as Stdin (Golang)

我正在尝试对类似于 this way 的 cli 应用程序进行功能测试。

由于该命令要求在命令提示符下进行一些输入,我将它们放入一个文件中并将其设置为 os.Stdin。

cmd := exec.Command(path.Join(dir, binaryName), "myArg")
tmpfile := setStdin("TheMasterPassword\nSecondAnswer\n12121212\n")
cmd.Stdin = tmpfile
output, err := cmd.CombinedOutput()

setStdin只是创建一个tmpFile,将字符串写入文件,returns *os.File.

现在,我希望 TheMasterPassword 成为第一个输入,它正在运行。但是对于第二个输入总是得到 Critical Error: EOF

我用来询问和获取用户输入的函数:

func Ask(question string, minLen int) string {
    reader := bufio.NewReader(os.Stdin)

    for {
        fmt.Printf("%s: ", question)

        response, err := reader.ReadString('\n')
        ExitIfError(err)

        if len(response) >= minLen {
            return strings.TrimSpace(response)
        } else {
            fmt.Printf("Provide at least %d character.\n", minLen)
        }
    }
}

你能帮我找出问题所在吗? 非常感谢!

按要求添加setStdin

func setStdin(userInput string) *os.File {
    tmpfile, err := ioutil.TempFile("", "test_stdin_")
    util.ExitIfError(err)

    _, err = tmpfile.Write([]byte(userInput))
    util.ExitIfError(err)
    _, err = tmpfile.Seek(0, 0)
    util.ExitIfError(err)

    return tmpfile
}

在您的应用程序中,只要您需要 输入行,您就可以调用 Ask()

Ask() 中创建一个 bufio.Reader to read from os.Stdin. Know that bufio.Reader–as its name suggests–uses buffered reading, meaning it may read more data from its source than what is returned by its methods (Reader.ReadString()。这意味着如果你只是用它来读取一行(或几行)并且你扔掉了reader,你将扔掉缓冲的,未读的数据

所以下次您再次调用 Ask() 并尝试从 os.Stdin 读取时,您将不会从中断的地方继续...

要解决此问题,只需从 os.Stdin 创建一个 bufio.Reader,例如将其存储在全局变量中,并在 Ask() 中始终使用这个 [=48] =].因此缓冲和未读数据不会在 Ask() 次调用之间丢失。当然,这个解决方案对于从多个 goroutines 调用是无效的,但是从单个 os.Stdin 读取也不是。

例如:

var reader = bufio.NewReader(os.Stdin)

func Ask(question string, minLen int) string {
    // use the global reader here...
}

另请注意,使用 bufio.Scanner would be easier in your case. But again, bufio.Scanner may also read more data from its source than needed, so you have to use a shared bufio.Scanner here too. Also note that Reader.ReadString() returns you a string containing the delimeter (a line ending with \n in your case) which you probably have to trim, while Scanner.Text()(使用默认的行拆分功能)将在返回行之前首先删除它。这也是您可以利用的简化。