在使用管道执行 go binary 时使用 scanln

我有以下代码:


package main


import "fmt"


func main() {

    if scanln_test() {

        fmt.Println("Success!")

    }

}


func scanln_test() bool {

    fmt.Print("Please type yes or no and then press enter [y/n]: ")


    var response string

    fmt.Scanln(&response)


    if response == "y" {

        return true

    } else if response == "n" {

        return false

    } else {

        return scanln_test()

    }

}

通过管道执行编译后的二进制文件时,例如:


$ echo "just-showing-how-pipe-affects-the-behavior" | ./scanln


我得到函数fmt.Print内部的无限输出。scanln_test但是,当我不通过管道执行它时,一切正常。


有没有办法解决这个问题?


UPD。当使用管道执行二进制文件时,fmt.Scanln 返回“EOF”作为错误


UPD2。上面带有 echo 的示例只是为了说明管道,但我不想在我的 go 程序中阅读此 echo 的内容。真实案例如下所示:wget -qO- http://example.com/get | sh -s "param". 我在这个下载的 shell 脚本中有执行 go 程序,我希望我的程序用 Y/N 向用户显示对话。我不确定这是否可能,所以现在我决定摆脱管道并像wget -qO- http://example.com/get | sh && go-program "param".


慕工程0101907
浏览 195回答 2
2回答

呼啦一阵风

因此,您需要来自 Stdin(管道)和用户 Stdin(键盘)的并发输入:我认为您的答案是 cat 命令,请参阅:如何将初始输入通过管道传输到进程中,然后该进程将是交互式的?和:https://en.wikipedia.org/wiki/Cat_(Unix)请参阅:如何在 Go 中通过管道传输多个命令?并进行进程间通信有 3 件事需要注意:首先:检查所有错误是一个好习惯:在您的情况下:n, err := fmt.Scanln(&response)&nbsp;&nbsp;第二:您正在使用递归调用(Tail Call),这里没有必要。用简单的 for 循环替换它并查看: Go3 中的尾调用优化:最后但并非最不重要的一点:如果输入错误,您的代码将永远循环(如果编译器无法优化尾调用,则消耗堆栈)!最好限制在 3 个。例如:package mainimport "fmt"import "strings"type Input intconst (&nbsp; &nbsp; Timeout Input = iota&nbsp; &nbsp; Yes&nbsp; &nbsp; No&nbsp; &nbsp; Abort&nbsp; &nbsp; BadInput)func userInput(msg string) Input {&nbsp; &nbsp; var input string&nbsp; &nbsp; for i := 0; i < 3; i++ {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(msg)&nbsp; &nbsp; &nbsp; &nbsp; n, err := fmt.Scanln(&input)&nbsp; &nbsp; &nbsp; &nbsp; if n > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch strings.ToLower(input) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "y":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Yes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "n":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return No&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "a":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Abort&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return BadInput // or panic(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return Timeout}func main() {&nbsp; &nbsp; ans := userInput("Please type Yes,No or Abort and then press enter [y/n/a]: ")&nbsp; &nbsp; fmt.Println(ans)&nbsp; &nbsp; switch ans {&nbsp; &nbsp; case Yes:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Yes") // do some job&nbsp; &nbsp; &nbsp; &nbsp; //...&nbsp; &nbsp; }}编辑:使用这个简单的“y/n”,您无需检查它是否是管道。即使是带有一个字节切片的简单 std Read 也很好:os.Stdin.Read(b1)查看我的管道示例:https ://stackoverflow.com/a/37334984/6169399但如果您的标准输入是管道,您可以使用:bytes, err := ioutil.ReadAll(os.Stdin)&nbsp;一次读取所有管道数据。但要小心处理错误。您可以检查标准输入是否与终端或管道相关联,然后使用适当的代码。检测它的简单方法是否是管道:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "os")func main() {&nbsp; &nbsp; info, err := os.Stdin.Stat()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("not a pipe")&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("pipe name=", info.Name(), "pipe size=", info.Size())&nbsp; &nbsp; }}全部在一个示例代码中:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "io/ioutil"&nbsp; &nbsp; "os"&nbsp; &nbsp; "strings")type Input intconst (&nbsp; &nbsp; Timeout Input = iota&nbsp; &nbsp; Yes&nbsp; &nbsp; No&nbsp; &nbsp; Abort&nbsp; &nbsp; BadInput)func userInput(msg string) Input {&nbsp; &nbsp; var input string&nbsp; &nbsp; for i := 0; i < 3; i++ {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(msg)&nbsp; &nbsp; &nbsp; &nbsp; n, err := fmt.Scanln(&input)&nbsp; &nbsp; &nbsp; &nbsp; if n > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch strings.ToLower(input) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "y":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Yes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "n":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return No&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "a":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Abort&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return BadInput // or panic(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return Timeout}func main() {&nbsp; &nbsp; info, err := os.Stdin.Stat()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; //fmt.Println("not a pipe")&nbsp; &nbsp; &nbsp; &nbsp; ans := userInput("Please type Yes,No or Abort and then press enter [y/n/a]: ")&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(ans)&nbsp; &nbsp; &nbsp; &nbsp; switch ans {&nbsp; &nbsp; &nbsp; &nbsp; case Yes:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Yes") // do some job&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //...&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("pipe name=", info.Name(), "pipe size=", info.Size())&nbsp; &nbsp; &nbsp; &nbsp; bytes, err := ioutil.ReadAll(os.Stdin)&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(string(bytes), err) //do some jobe with bytes&nbsp; &nbsp; }}

明月笑刀无情

您可以使用os.ModeCharDevice:stat, _ := os.Stdin.Stat()if (stat.Mode() & os.ModeCharDevice) == 0 {&nbsp; &nbsp; // piped&nbsp; &nbsp; input, _ := ioutil.ReadAll(os.Stdin)} else {&nbsp; &nbsp; // not piped, do whatever, like fmt.Scanln()}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go