我有以下代码:
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"
那样单独运行。
答案 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()
}