我正在尝试对类似于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,在文件中写入字符串并返回*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
}
答案 0 :(得分:2)
在您的应用中,只要您想要一个单个输入行,就好像您的呼叫Ask()
。
在Ask()
内,您创建了一个bufio.Reader
以从os.Stdin
读取。知道bufio.Reader
(顾名思义)使用缓冲读取,这意味着它从其源读取的数据可能比其方法返回的数据更多(在这种情况下为Reader.ReadString()
)。这意味着,如果仅使用它来读取一行(或几行),然后扔掉阅读器,那么<em>您将丢弃未读取的缓冲数据。
因此,下次您再次呼叫Ask()
并尝试从os.Stdin
进行读取时,您将不会从上次中断的地方继续...
要解决此问题,请仅从bufio.Reader
创建一个os.Stdin
,并将其存储在全局变量中,例如,在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
可能还会从源中读取更多数据,因此,您在这里也必须使用共享的bufio.Scanner
。还要注意,Reader.ReadString()
返回一个包含定界符(在您的情况下,以\n
结尾的行)的字符串,而您可能需要修剪,而Scanner.Text()
(具有默认的行拆分功能)将在返回行之前先剥离该行。这也是您可以利用的一种简化。