Using Shell Pipes with Go

⭐️ 👀 2023 WEB DEVELOPMENT BOOTCAMP starting in days! Join the waiting list to reserve your spot in my 10-weeks cohort course and learn the fundamentals, HTML, CSS, JS, Tailwind, React, Next.js and much much more! 👀 ⭐️

Being a good citizen in the command line means using pipes.

Using the vertical bar | we can pass the output of a command as an input of another command, and chain multiple commands to provide a unique output.

I have made 2 tutorials that use pipes as their input, building gocowsay and gololcat, but I didn’t describe in detail the process to get input from a pipe, so here’s an article on this topic.

Here’s an example:

package main

import (

func main() {
	info, err := os.Stdin.Stat()
    if err != nil {

	if info.Mode()&os.ModeCharDevice != 0 || info.Size() <= 0 {
		fmt.Println("The command is intended to work with pipes.")
		fmt.Println("Usage: fortune | gocowsay")

	reader := bufio.NewReader(os.Stdin)
	var output []rune

	for {
		input, _, err := reader.ReadRune()
		if err != nil && err == io.EOF {
		output = append(output, input)

	for j := 0; j < len(output); j++ {
		fmt.Printf("%c", output[j])

Let’s examine how this works. First interesting line:

info, err := os.Stdin.Stat()

os.Stdin, like Stdout and Stderr, is an open File. It points to the standard input file descriptor.

Stat() is a method of File that returns a FileInfo describing the file, providing information including mode and file size.

The next interesting piece is

info.Mode()&os.ModeCharDevice != 0

FileInfo.Mode() returns the uint32 mask that determines the file mode and permissions as a const (

Usign a bitwise AND determines if the file mode is os.ModeCharDevice. This is a way to make sure that the file points to a ModeCharDevice, the Unix character device (terminal). Since the file points to os.Stdin, We’re basically excluding the input being a pipe.

Bitwise returns 0 if the mode is not the constant we pass. We could also check for

info.Mode()&os.ModeCharDevice == os.ModeCharDevice

that’s the same, but more readable.

If this is false, we have an additional check for

info.Size() <= 0

which combined with the previous check, makes sure have an input pipe, and that actually contains some bytes.

We could also check for

if info.Mode()&os.ModeNamedPipe != 0 {
	// we have a pipe input

and this will make sure the input comes from the pipe.

Next the program creates a reader

reader := bufio.NewReader(os.Stdin)

bufio is a package that implements buffered I/O, basically wrapping io.Reader and io.Writer.

We ask it to give us a buffered reader from os.Stdin, using the default buffer size which is 4096 bytes.

bufio.Reader provides many methods to read data: ReadByte, ReadBytes, ReadLine, ReadRune, ReadSlice, ReadString.

The example uses ReadRune so it can add each rune read into the var output []rune slice, and handle unicode chars without any effort.

Read more

One more thing! ⚠️ ✋

At the end of January I will organize the Web Development Bootcamp.

It's a 10-weeks long cohort online course where I will guide you to becoming a Web Developer.

It's not "just a course". It's a big event I organize once a year.

We'll start from zero, learn the fundamentals of Web Development, HTML CSS, JavaScript, Tailwind, Git, using the command line, VS Code, GitHub, Node.js, we'll then learn React, JSX, how to use PostgreSQL, Astro, Next.js, Prisma, deploying on Netlify/DigitalOcean/Fly/Vercel and much more! 

At the end of the first 10 weeks you'll know how to create web sites and web applications and I'll unlock you the 2nd phase of the Bootcamp: you will get access to a large number of projects exclusive to the Bootcamp graduates, so you can follow my instructions to build things like private areas with authentication, clones of popular sites like Twitter YouTube Reddit, create e-commerce sites, and much much more.

Because once you got the fundamentals, you only learn by working on real, exciting projects.

To find out more, visit