我在Golang中有以下功能:
func ReadFromStdinIfAvailable(cmd *cobra.Command, args []string) ([]byte, error) {
if len(args) == 0 {
r := bufio.NewReader(os.Stdin)
firstByte, err := r.Peek(1)
if err != nil {
return nil, err
}
if len(firstByte) == 0 {
cmd.Println(userstrings.NoFilenameProvidedErrorString)
return nil, errors.New(userstrings.NoFilenameProvidedErrorString)
}
var bytesResult []byte
scanner := bufio.NewScanner(r)
for scanner.Scan() {
// Read from stdin - allocate a buffer to read into
bytesResult = append(bytesResult, scanner.Bytes()...)
}
return bytesResult, nil
}
return nil, errors.New(userstrings.NoStdInProvidedErrorString)
}
数据量非常小(<10 MB to be sure). However, the data should be there from the first read - if it's not, there's an error.
但是,当我这样做时,它会挂在
scanner.Scan()
上,这是一个流。
在开始阅读之前,如何查看
os.Stdin
流中是否有任何数据?
答案是从流中读取前
n
字节,然后创建一个新的 io.Reader
,其中包含您已读取的相同数据,然后是流中的剩余数据。
这里是显示此示例的游乐场链接。
reader := strings.NewReader("i am the very model of a modern major general")
buf := make([]byte, 10) // Read first 10 bytes
_, _ = io.ReadAtLeast(reader, buf, 10)
fmt.Println(string(buf))
// Make a new stream that concats the data you read with what is remaining
newReader := io.MultiReader(bytes.NewReader(buf), reader)
data, _ := io.ReadAll(newReader)
fmt.Println(string(data))
这就是我最终所做的 - 如果这对任何人有帮助(希望有人做得更好):
func ReadFromStdinIfAvailable(cmd *cobra.Command, args []string) ([]byte, error) {
if len(args) == 0 {
r := bufio.NewReader(RootCmd.InOrStdin())
var bytesResult []byte
scanner := bufio.NewScanner(r)
// buffered channel of dataStream
dataStream := make(chan []byte, 1)
// Run scanner.Bytes() function in it's own goroutine and pass back it's
// response into dataStream channel.
go func() {
for scanner.Scan() {
dataStream <- scanner.Bytes()
}
close(dataStream)
}()
// Listen on dataStream channel AND a timeout channel - which ever happens first.
timedOut := false
select {
case res := <-dataStream:
bytesResult = append(bytesResult, res...)
case <-time.After(time.Duration(10) * time.Millisecond): //nolint:gomnd // 10ms timeout
timedOut = true
}
if timedOut {
cmd.Println(userstrings.NoFilenameProvidedErrorString)
return nil, errors.New(userstrings.NoFilenameProvidedErrorString)
}
for scanner.Scan() {
bytesResult = append(bytesResult, scanner.Bytes()...)
}
return bytesResult, nil
}
return nil, fmt.Errorf("should not be possible, args should be empty")
}