为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕工程0101907 2022-01-17 10:11:50
我有以下代码:package mainimport "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".
查看完整描述

2 回答

?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

因此,您需要来自 Stdin(管道)和用户 Stdin(键盘)的并发输入:

我认为您的答案是 cat 命令,请参阅:如何将初始输入通过管道传输到进程中,然后该进程将是交互式的?

和:https://en.wikipedia.org/wiki/Cat_(Unix)


请参阅:如何在 Go 中通过管道传输多个命令?

并进行进程间通信


有 3 件事需要注意:

首先:检查所有错误是一个好习惯:

在您的情况下:


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

第二:

您正在使用递归调用(Tail Call),这里没有必要。

用简单的 for 循环替换它并查看: Go

3 中的尾调用优化:

最后但并非最不重要的一点:如果输入错误,您的代码将永远循环(如果编译器无法优化尾调用,则消耗堆栈)!

最好限制在 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

        //...

    }

}

编辑:使用这个简单的“y/n”,您无需检查它是否是管道。

即使是带有一个字节切片的简单 std Read 也很好:


os.Stdin.Read(b1)

查看我的管道示例:https ://stackoverflow.com/a/37334984/6169399


但如果您的标准输入是管道,您可以使用:


bytes, err := ioutil.ReadAll(os.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

    }


}



查看完整回答
反对 回复 2022-01-17
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

您可以使用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()

}


查看完整回答
反对 回复 2022-01-17
  • 2 回答
  • 0 关注
  • 194 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号