Last Updated: September 25, 2020
·
25.2K
· Tarcísio Xavier Gruppi

golang: having fun with os.Stdin and shell pipes

How can we know when our app have a piped stdin or not?

os.Pipe is a os.File so we can use File.Stat() which return a os.FileInfo and we can use FileInfo.Size() to see if there is any data available to read.

package main

import  (
  "os"
  "fmt"
)

func main() {
  fi, err := os.Stdin.Stat()
  if err != nil {
    panic(err)
  }
  if fi.Size() > 0 {
    fmt.Println("there is something to read")
  } else {
    fmt.Println("stdin is empty")
  }
}

This will work with $ date | go run main.go but not $ curl http://www.google.com/ | go run main.go.

Since curl need some time to send data we will always get 0 (zero) for FileInfo.Size().

To make it work we have to use FileInfo.Mode().

package main

import (
  "os"
  "fmt"
)

func main() {
  fi, err := os.Stdin.Stat()
  if err != nil {
    panic(err)
  }
  fmt.Printf("%v\n", fi.Mode())
}
$ go run main.go
Dcrw--w----
$ date | go run main.go
prw-rw----

What is this?

File.Mode() return a FileMode flag.

You can see what is each letter here FileMode.

The flag we are looking for is os.ModeNamedPipe. When this flag is on it means that we have a pipe.

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

This way we can know when our command is receiving stdout from another process.

If you have any other ideia comment it.

You can comment about my english mistakes too so i can learn.

Thank you.

3 Responses
Add your response

I'm still learning golang myself. Thanks for sharing - I learned something new today!

over 1 year ago ·

I'm learning golang too. Do you know http://exercism.io/ ? It's a great place to practice. Thank you.

over 1 year ago ·

Thank you. This article really helped me.

over 1 year ago ·