11

Is there a way to check if the input stream (os.Stdin) has data?

The post Read from initial stdin in GO? shows how to read the data, but unfortunately blocks if no data is piped into the stdin.

Community
  • 1
  • 1
mauvm
  • 3,586
  • 3
  • 18
  • 19
  • 1
    What is the action taken once you have data available in stdin? I think it's better to start a goroutine that blocks on input. – Rumple Stiltskin Mar 23 '14 at 07:09
  • If you want to avoid blocking while reading stdin when no data has been piped into stdin, you can [check if stdin is a terminal](https://stackoverflow.com/a/74478987/11210494). – thepudds Nov 29 '22 at 20:36

2 Answers2

16

os.Stdin is like any other "file", so you can check it's size:

package main

import (
    "fmt"
    "os"
)

func main() {
    file := os.Stdin
    fi, err := file.Stat()
    if err != nil {
        fmt.Println("file.Stat()", err)
    }
    size := fi.Size()
    if size > 0 {
        fmt.Printf("%v bytes available in Stdin\n", size)
    } else {
        fmt.Println("Stdin is empty")
    }
}

I built this as a "pipe" executable, here is how it works:

$ ./pipe
Stdin is empty
$ echo test | ./pipe
5 bytes available in Stdin
Kluyg
  • 5,119
  • 2
  • 25
  • 28
  • 2
    I'm not convinced this is correct. It seems to me it is depending on the behavior of fstat on pipes. I can't find any reference, but from experience I'd say this is the kind of thing that is poorly specified by posix. For example, if there are more bytes available on stdin than the pipe buffering size (4096 on Linux) then will it return 4096? Thought experiment: What if the writer to the pipe is sending 10 bytes per second? Will fstat return 10, 20, etc as the buffer fills up? But then when the buffer is full and the writer is sleeping, what happens? – Jeff Allen Mar 24 '14 at 17:38
  • 3
    This isn't reliable if the writer doesn't write data immediately. Eg `(sleep 1; echo "hello" ) | ./pipe` prints `Stdin is empty` which it clearly isn't. – Nick Craig-Wood Apr 01 '14 at 19:42
  • 3
    @NickCraig-Wood I agree, I've upvoted your answer http://stackoverflow.com/a/22748517/91570. It would be nice to merge those two questions. – Kluyg Apr 02 '14 at 03:26
  • 1
    `echo hi | ./main` does not work with this answer – Alexander Aug 01 '22 at 13:27
8

This seems to be reliable solution and works even with sleep/delayed data via pipe. https://coderwall.com/p/zyxyeg/golang-having-fun-with-os-stdin-and-shell-pipes

package main

import (
  "os"
  "fmt"
)

func main() {
  fi, err := os.Stdin.Stat()
  if err != nil {
    panic(err)
  }
  if fi.Mode() & os.ModeNamedPipe == 0 {
    fmt.Println("no pipe :(")
  } else {
    fmt.Println("hi pipe!")
  }
}
PetrD
  • 89
  • 1
  • 1
  • 1
    Another post suggests checking `ModeCharDevice` which works for me. With `ModeNamedPipe` I have to check `!= 0` (on Linux) https://stackoverflow.com/a/26567513/270302 – Avindra Goolcharan Jul 11 '21 at 16:47