将文件用作标准输入(Golang)时在第二次提示时获取EOF

将文件用作标准输入(Golang)时在第二次提示时获取EOF,go,integration-testing,Go,Integration Testing,我正在尝试对类似的cli应用程序进行功能测试 当命令在命令提示符下请求一些输入时,我将它们放在一个文件中,并将其设置为os.Stdin cmd := exec.Command(path.Join(dir, binaryName), "myArg") tmpfile := setStdin("TheMasterPassword\nSecondAnswer\n12121212\n") cmd.Stdin = tmpfile output, err := cmd.CombinedOutput() s

我正在尝试对类似的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,将字符串写入文件并返回
*os.file

现在,我希望主密码是第一个输入,并且它正在工作。但对于第二个输入,总是出现
严重错误: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
——顾名思义——使用缓冲读取,这意味着它从源中读取的数据可能比其方法返回的数据多(在本例中)。也就是说,如果你只是用它来读一行(或几行),而你扔掉了读卡器,你就会扔掉缓冲的、未读的数据

因此,下次您再次调用
Ask()
,尝试从
os.Stdin
读取时,您将无法从中断处继续

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

例如:

var reader = bufio.NewReader(os.Stdin)

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

还请注意,在您的情况下使用会更容易。但是,同样地,
bufio.Scanner
也可能从其源读取比需要更多的数据,因此您也必须在此处使用共享的
bufio.Scanner
。还请注意,
Reader.ReadString()
返回一个包含delimeter的字符串(在您的情况下是以
\n
结尾的一行),您可能需要对其进行修剪,而(使用默认的行拆分功能)将在返回行之前先去除该字符串。这也是你可以利用的一种简化方法。

在你的应用程序中,只要你需要一条输入线,你的呼叫就很像你的呼叫
Ask()

Ask()。要知道,
bufio.Reader
——顾名思义——使用缓冲读取,这意味着它从源中读取的数据可能比其方法返回的数据多(在本例中)。也就是说,如果你只是用它来读一行(或几行),而你扔掉了读卡器,你就会扔掉缓冲的、未读的数据

因此,下次您再次调用
Ask()
,尝试从
os.Stdin
读取时,您将无法从中断处继续

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

例如:

var reader = bufio.NewReader(os.Stdin)

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

还请注意,在您的情况下使用会更容易。但是,同样地,
bufio.Scanner
也可能从其源读取比需要更多的数据,因此您也必须在此处使用共享的
bufio.Scanner
。还请注意,
Reader.ReadString()
返回一个包含delimeter的字符串(在您的情况下是以
\n
结尾的一行),您可能需要对其进行修剪,而(使用默认的行拆分功能)将在返回行之前先去除该字符串。这也是您可以利用的简化。

向我们展示您的应用程序如何从其标准输入读取数据,以及(以防万一)设置TDIN()
实现。瞄准a。将命令更改为
cat
,查看
setStdin
是否工作。如果它能工作,那就是你的cli应用程序的问题,我们需要看看它是如何从stdin中使用的。@leadbebop,我想,它能工作(至少在最初),因为我得到的第一个答案是正确的@icza,添加了
setStdin
谢谢你们两位@我对此表示怀疑。尝试在
Seek
之前添加
tmpfile.Sync()
。在这种情况下,我认为
bytes.Buffer
比temp文件好得多。请向我们展示您的应用程序如何读取其标准输入,以及(以防万一)
setStdin()
实现。瞄准a。将命令更改为
cat
,查看
setStdin
是否工作。如果它能工作,那就是你的cli应用程序的问题,我们需要看看它是如何从stdin中使用的。@leadbebop,我想,它能工作(至少在最初),因为我得到的第一个答案是正确的@icza,添加了
setStdin
谢谢你们两位@我对此表示怀疑。尝试在
Seek
之前添加
tmpfile.Sync()
。在这种情况下,我认为
bytes.Buffer
比临时文件好得多。非常好用!非常感谢你!顺便说一句,使用
strings.NewReader
而不是tmpFile,它非常方便+1工作完美!非常感谢你!顺便说一句,使用
strings.NewReader
而不是tmpFile,它非常方便+1