在Golang中检查标准输入(STDIN)是否有可读内容。

huangapple go评论77阅读模式
英文:

Check if there is something to read on STDIN in Golang

问题

我需要一个命令行实用程序,如果有一些字符串被输入到其标准输入(STDIN)中,它会有不同的行为。这里是一个简单的示例:

package main // 文件 test.go

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

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

	if len(bytes) > 0 {
		fmt.Println("STDIN 中有内容:" + string(bytes))
	} else {
		fmt.Println("STDIN 中没有内容")
	}
}

如果像这样调用它,它可以正常工作:

echo foo | go run test.go

如果在没有任何内容输入到 STDIN 的情况下调用 test.go,程序会在以下代码处停止:

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

等待 EOF

我需要做什么才能解决这个问题?

提前谢谢!

英文:

I need a command line utility to behave different if some string is piped into its STDIN. Here's some minimal example:

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")
	}
}

This works fine if you call it like that:

echo foo | go run test.go

If test.go is called without anything on STDIN, the thing stucks at...

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

... waiting for EOF.

What do I need to do to get this going?

Thanks in advance!

答案1

得分: 62

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

stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
    fmt.Println("数据正在通过stdin进行传输")
} else {
    fmt.Println("stdin来自终端")
}
英文:

I solved this by using os.ModeCharDevice:

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")
}

答案2

得分: 20

使用code.google.com/p/go.crypto/ssh/terminal中的IsTerminal函数(以前是exp/terminal)或github.com/andrew-d/go-termutil中的Isatty函数。

如果stdin是终端/tty,则表示没有通过管道输入内容,你可以执行不同的操作。

以下是一个示例:

package main

import (
	"fmt"
	termutil "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)
	}
}

测试:

$ ./isatty 
Nothing on STDIN
$ echo "hello" | ./isatty 
Something on STDIN
hello
$ (sleep 1 ; echo "hello") | ./isatty 
Something on STDIN
hello
英文:

Use the IsTerminal function from code.google.com/p/go.crypto/ssh/terminal (which was exp/terminal) or the Isatty function from github.com/andrew-d/go-termutil
which is a much more focussed package.

If stdin is a terminal/tty then you aren't being piped stuff and you can do something different.

Here is an example

package main

import (
	"fmt"
	termutil "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)
	}
}

Testing

$ ./isatty 
Nothing on STDIN
$ echo "hello" | ./isatty 
Something on STDIN
hello
$ (sleep 1 ; echo "hello") | ./isatty 
Something on STDIN
hello

答案3

得分: 3

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

stat, err := os.Stdin.Stat()
if err != nil {
    return nil, fmt.Errorf("stdin 出现错误:%s", err)
}
if (stat.Mode() & os.ModeNamedPipe) == 0 {
    return nil, errors.New("您应该向 stdin 传递一些内容")
}

在我的测试中,这种方法在 darwin(Mac OS)和 linux(Ubuntu)上都有效。

英文:

If none of the above works for you, try this way:

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")
}

It worked for me in both darwin (Mac OS) and linux (Ubuntu).

答案4

得分: 0

这就是它的代码:

package main // 文件 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("STDIN 上的内容:", string(in))
    } else {
        fmt.Println("STDIN 上没有内容")
    }
}

谢谢 @Kluyg!

英文:

This does it:

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")
    }
}

Thanks @Kluyg !

答案5

得分: 0

这里的其他答案似乎在Windows上的Git Bash下不起作用,而Git Bash是GitHub Actions在Windows上的默认bash。来自golang.org/x/term的isTerminal也似乎不起作用。

对我来说,使用https://github.com/mattn/go-isatty 在包括Windows上的Git Bash / cygwin / MinGW在内的各个平台上似乎都可以工作。

示例用法(playground链接):

func main() {
	if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) {
		fmt.Println("stdin是终端,os.Stdin上没有内容")
		return
	}

	// stdin可能有数据,也可能没有数据,但是由于它不是终端,
	// 我们应该能够从中读取。
	//
	// 一些示例:
	//    echo foo | go run .     # 我们可以从os.Stdin读取"foo"
	//    go run . < /dev/null    # 我们可以读取os.Stdin,但它是空的
	fmt.Println("stdin不是终端,我们可以读取它")
	io.Copy(os.Stdout, os.Stdin)
}

请注意,这是回答“我可以从os.Stdin读取吗(可能是零长度)?”而不是“os.Stdin中是否有非空数据?”至少对于我的用例来说,第一种形式的问题是有用的,其中“零长度数据”是一种可以读取的输入数据类型。

我很想知道它是否对其他人不起作用(包括使用的平台)。

英文:

The other answers here did not seem to work under Git Bash on Windows, which is the default bash for GitHub Actions on Windows. isTerminal from golang.org/x/term also did not seem to work.

For me, using https://github.com/mattn/go-isatty seems to work across platforms, including Git Bash / cygwin / MinGW on Windows.

Example usage (playground link):

func main() {
	if isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) {
		fmt.Println(&quot;stdin is terminal, nothing on os.Stdin&quot;)
		return
	}

	// stdin might or might not have data, but given it is not a terminal,
	// we should be able to read from it.
	//
	// Some examples:
	//    echo foo | go run .     # we can read &quot;foo&quot; from os.Stdin
	//    go run . &lt; /dev/null    # we can read os.Stdin, but it is empty
	fmt.Println(&quot;stdin is not a terminal and we can read it&quot;)
	io.Copy(os.Stdout, os.Stdin)
}

Note that this is answering "Can I read from os.Stdin (which might be zero length)?", and not "Is there non-empty data in os.Stdin?". At least for my use case, the first form of the question is useful where "zero length data" is a type of input data that is OK to read.

I would be curious to hear if it does not work for someone else (including what platform).

huangapple
  • 本文由 发表于 2014年3月30日 21:18:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/22744443.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定