我想阅读Go程序的原始stdin
。例如,如果我做echo test stdin | go run test.go
,我希望能够访问“test stdin”。我试过从os.Stdin
读取,但是如果它没有任何内容,那么它将等待输入。我还尝试首先检查大小,但即使传入输入,os.Stdin.Stat().Size()
也为0.
我该怎么办?
答案 0 :(得分:65)
使用os.Stdin
从标准输入读取应该按预期工作:
package main
import "os"
import "log"
import "io/ioutil"
func main() {
bytes, err := ioutil.ReadAll(os.Stdin)
log.Println(err, string(bytes))
}
执行echo test stdin | go run stdin.go
应该打印'测试stdin'很好。
如果您附加用于识别遇到的问题的代码,将会有所帮助。
对于基于行的阅读,您可以使用bufio.Scanner
:
import "os"
import "log"
import "bufio"
func main() {
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
log.Println("line", s.Text())
}
}
答案 1 :(得分:19)
我认为你的问题本身没有明智的答案,因为没有“初始标准”这样的事情。类Unix操作系统和Windows实现了"standard streams"的概念,它的工作方式与此类似(简化):创建进程时,它自动拥有三个文件描述符(Windows中的句柄)open - stdin,stdout和stderr。毫无疑问,你熟悉这个概念,但我想强调“流”这个词的意思 - 在你的例子中,当你打电话时
$ echo 'test stdin' | ./stdin
shell创建一个pipe,生成两个进程(一个用于echo
,一个用于二进制)并使用它创建的管道:管道的写FD连接到{{1 stdout和管道的读FD连接到二进制文件的stdin。然后,无论echo
进程请写到它的标准输出是什么,用管道输入(原文!)到你的进程的标准输入。
(实际上,今天的大多数shell都会将echo
实现为内置原语,但这并不会改变语义;你也可以尝试使用echo
,这是一个真正的程序。我刚刚使用/bin/echo
来引用您的程序 - 这是为了清晰起见,因为./stdin
最终会做到这一点。)
请注意以下几项重要事项:
go run stdin.go
)并不是要将任何内容写入其标准输出(例如,echo
不会向其标准输出写入任何内容并成功退出)。echo -n
后才会失败)。让我们把它包起来:你正在观察的行为是正确和正常的。如果您希望从stdin获取任何数据,则不能指望它随时可用。如果你也不想在stdin上阻塞,那么创建一个goroutine,它会在无限循环中阻止从stdin读取(但检查EOF条件)并通过一个通道传递收集的数据(可能在某些处理之后,如果需要)。
1 这就是为什么通常在管道中的两个管道之间出现的某些工具(例如read
)可能有特殊选项使它们在写完每一行后刷新它们的stdout - 读取关于一个示例中grep
manual page中的grep
选项。那些没有意识到这种“默认完全缓冲”语义的人很困惑,为什么--line-buffered
似乎停滞不前,而且当显而易见的受监控文件被更新时,不会显示任何内容。
作为旁注:如果您按“原样”运行二进制文件,例如
tail -f /path/to/some/file.log | grep whatever | sed ...
这并不意味着生成的进程不会有stdin(或“initial stdin”或whaveter),相反,它的stdin将连接到shell接收键盘导入的同一个流(所以你可以直接键入一些东西到你的进程的stdin)。
将进程的stdin连接到无处的唯一可靠方法是使用
$ ./stdin
在类Unix操作系统和
上$ ./stdin </dev/null
在Windows上。这个"null device"使得该过程在其stdin的第一个C:\> stdin <NUL
上看到EOF。
答案 2 :(得分:5)
您无法检查stdin的内容,但您可以检查stdin是否与终端或管道相关联。 IsTerminal只采用标准的unix fd数字(0,1,2)。 syscall包已分配变量,因此如果您更喜欢命名它们,可以执行syscall.Stdin。
package main
import (
"code.google.com/p/go.crypto/ssh/terminal"
"fmt"
"io/ioutil"
"os"
)
func main() {
if ! terminal.IsTerminal(0) {
b, _ := ioutil.ReadAll(os.Stdin)
fmt.Print(string(b))
} else {
fmt.Println("no piped data")
}
}