在执行带管道的二进制文件时使用scanln

时间:2016-05-31 06:32:18

标签: go pipe

我有以下代码:

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。当使用in pipe

执行二进制文件时,fmt.Scanln返回“EOF”作为错误

UPD2。上面回显的例子只是为了说明管道,但我不打算在我的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"那样单独运行。

2 个答案:

答案 0 :(得分:1)

所以你需要来自Stdin(管道)和用户Stdin(键盘)的并发输入:
 我想你回答的是cat命令:How can I pipe initial input into process which will then be interactive?
并且:https://en.wikipedia.org/wiki/Cat_(Unix)

见:How to pipe several commands in Go?
Go Inter-Process Communication

有三点需要注意:
第一:检查所有错误是一种好习惯:
  在你的情况下:

n, err := fmt.Scanln(&response)  

第二:
你正在使用递归呼叫(尾调用),这里没有必要 用简单的for循环替换它,请参阅:Tail Call Optimization in Go
第三:
最后但并非最不重要:如果输入错误,您的代码将永远循环(如果编译器无法优化尾调用,则会消耗堆栈)! 最好限制在3.
例如:

package main

import "fmt"
import "strings"

type Input int

const (
    Timeout Input = iota
    Yes
    No
    Abort
    BadInput
)

func userInput(msg string) Input {
    var input string
    for i := 0; i < 3; i++ {
        fmt.Println(msg)
        n, err := fmt.Scanln(&input)
        if n > 0 {
            switch strings.ToLower(input) {
            case "y":
                return Yes
            case "n":
                return No
            case "a":
                return Abort
            }
        }
        if err != nil {
            return BadInput // or panic(err)
        }
    }
    return Timeout
}
func main() {
    ans := userInput("Please type Yes,No or Abort and then press enter [y/n/a]: ")
    fmt.Println(ans)
    switch ans {
    case Yes:
        fmt.Println("Yes") // do some job
        //...
    }
}

编辑: 用这个简单的&#34; y / n&#34;你不需要检查管道是否是管道 即使是简单的std Read也不错:

os.Stdin.Read(b1)

查看我的管道样本:https://stackoverflow.com/a/37334984/6169399

但是如果您的Stdin是管道,您可以使用:

bytes, err := ioutil.ReadAll(os.Stdin) 

一次性读取所有管道数据。但要小心处理错误。 你可以检查stdin是否与终端或管道相关联,然后使用适当的代码 检测它的简单方法是管道与否:

package main

import (
    "fmt"
    "os"
)

func main() {
    info, err := os.Stdin.Stat()
    if err != nil {
        fmt.Println("not a pipe")
    } else {
        fmt.Println("pipe name=", info.Name(), "pipe size=", info.Size())
    }
}

所有示例代码:

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

type Input int

const (
    Timeout Input = iota
    Yes
    No
    Abort
    BadInput
)

func userInput(msg string) Input {
    var input string
    for i := 0; i < 3; i++ {
        fmt.Println(msg)
        n, err := fmt.Scanln(&input)
        if n > 0 {
            switch strings.ToLower(input) {
            case "y":
                return Yes
            case "n":
                return No
            case "a":
                return Abort
            }
        }
        if err != nil {
            return BadInput // or panic(err)
        }
    }
    return Timeout
}
func main() {
    info, err := os.Stdin.Stat()
    if err != nil {
        //fmt.Println("not a pipe")
        ans := userInput("Please type Yes,No or Abort and then press enter [y/n/a]: ")
        fmt.Println(ans)
        switch ans {
        case Yes:
            fmt.Println("Yes") // do some job
            //...
        }

    } else {
        fmt.Println("pipe name=", info.Name(), "pipe size=", info.Size())
        bytes, err := ioutil.ReadAll(os.Stdin)
        fmt.Println(string(bytes), err) //do some jobe with bytes
    }

}

答案 1 :(得分:0)

您可以使用os.ModeCharDevice

stat, _ := os.Stdin.Stat()

if (stat.Mode() & os.ModeCharDevice) == 0 {
    // piped
    input, _ := ioutil.ReadAll(os.Stdin)
} else {
    // not piped, do whatever, like fmt.Scanln()
}