如果某些字符串通过管道传递到其STDIN,则我需要一个命令行实用程序来表现不同。 这是一些最小的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main // file test.go

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

func main() {
    bytes, _ := ioutil.ReadAll(os.Stdin)

    if len(bytes) > 0 {
        fmt.Println("Something on STDIN:" + string(bytes))
    } else {
        fmt.Println("Nothing on STDIN")
    }
}

如果您这样称呼它,效果很好:

1
echo foo | go run test.go

如果在STDIN上没有任何调用test.go,则该事件卡在...

1
bytes, _ := ioutil.ReadAll(os.Stdin)

...等待EOF

我需要怎么做才能做到这一点?

提前致谢!

  • 您是否尝试用bufio.reader或类似的东西包装stdin?还是用偷看看看是否有什么要读书的?
  • 阅读文档:ReadAll一直进行到出现错误或EOF为止,因此请问自己:从stdin读取时是否出错? EOF? (您可以在终端中发送EOF,在Unix上按Control-D,在Windows上进行其他操作)
  • @loreb我阅读了文档。您描述的是我做过的同样的事情,没有提到任何新内容。
  • @Not_a_Golfer虐待尝试一下,到目前为止谢谢
  • @sontags嗯?对不起,那我一定是误解了你的问题。它没有看到EOF,因为很明显,键盘仍然存在,因此您必须从键盘(Unix中的control-D)发送EOF或一次一行一行地读取输出。
  • @loreb对不起,可能我还不够清楚:我知道这样的事实,为了通过该ReadAll,必须有一个EOF或一个错误。是的,CTRL + D发送EOF,但这不是可用性的选择。因此,很可能ReadAll是错误的方法,这只是为了说明结果。我基本上要求提供一些提示,以实现预期的行为。
  • 确定Stdin是否具有Go数据的可能重复项

我通过使用os.ModeCharDevice解决了这个问题:

1
2
3
4
5
6
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
    fmt.Println("data is being piped to stdin")
} else {
    fmt.Println("stdin is from a terminal")
}
  • 不确定这是否适用,例如/ dev / null
  • 经过测试,如果您从/ dev / null传递标准输入(即./foo
  • 我比@ NickCraig-Wood更喜欢此解决方案,因为它使用标准的go包。

使用code.google.com/p/go.crypto/ssh/terminal(为exp/terminal)中的IsTerminal函数或github.com/andrew-d/go-termutil中的Isatty函数
这是一个更加集中的软件包。

如果stdin是一个终端/ tty,那么您就不是管道用户了,您可以做一些不同的事情。

这是一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
   "fmt"
   "github.com/andrew-d/go-termutil"
   "io"
   "os"
)

func main() {
    if termutil.Isatty(os.Stdin.Fd()) {
        fmt.Println("Nothing on STDIN")
    } else {
        fmt.Println("Something on STDIN")
        io.Copy(os.Stdout, os.Stdin)
    }
}

测试中

1
2
3
4
5
6
7
8
$ ./isatty
Nothing on STDIN
$ echo"hello" | ./isatty
Something on STDIN
hello
$ (sleep 1 ; echo"hello") | ./isatty
Something on STDIN
hello
  • 你能举个例子吗?
  • 提供@sontags示例!

如果以上方法都不适合您,请尝试以下方式:

1
2
3
4
5
6
7
stat, err := os.Stdin.Stat()
if err != nil {
    return nil, fmt.Errorf("you have an error in stdin:%s", err)
}
if (stat.Mode() & os.ModeNamedPipe) == 0 {
    return nil, errors.New("you should pass smth to stdin")
}

它在darwin(Mac OS)和linux(Ubuntu)中都对我有用。

  • $ go run sotest.go < sotest.go产生you should pass smth to stdinModeCharDevice的答案行为符合预期。

这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main // file test.go

import (
   "bufio"
"fmt"
   "os"
)

func main() {
    in := bufio.NewReader(os.Stdin)
    stats, err := os.Stdin.Stat()
    if err != nil {
        fmt.Println("file.Stat()", err)
    }

    if stats.Size() > 0 {
        in, _, err := in.ReadLine()
        if err != nil {
            fmt.Println("reader.ReadLine()", err)
        }
        fmt.Println("Something on STDIN:" + string(in))
    } else {
        fmt.Println("Nothing on STDIN")
    }
}

谢谢@Kluyg!

  • ( sleep 1 ; echo"hello" ) | .test不起作用,打印Nothing on STDIN。 我认为您不会获得这种方法来可靠地工作。