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.
Written by Tarcísio Gruppi
Related protips
3 Responses
I'm still learning golang myself. Thanks for sharing - I learned something new today!
I'm learning golang too. Do you know http://exercism.io/ ? It's a great place to practice. Thank you.
Thank you. This article really helped me.