将文件用作标准输入(Golang)时在第二次提示时获取EOF
我正在尝试对类似的cli应用程序进行功能测试 当命令在命令提示符下请求一些输入时,我将它们放在一个文件中,并将其设置为os.Stdin将文件用作标准输入(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
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