Reading Data from `stdin` in Go
When writing a utility program that can be used in a pipeline, reading data from stdin
(standard input) is often required. This allows your program to accept input from other programs or from files, making it more flexible and versatile.
In Go, you can use the bufio
package to read from stdin
. The bufio
package offers buffered I/O operations, which are typically more efficient than reading data directly from os.Stdin
, especially when handling large inputs or frequent reads.
Basic Example: Reading and Printing Input
The simplest case is reading input line by line from stdin
and printing it to the console. This can be done with a Scanner
from the bufio
package. Here’s a basic example:
How it works:
We create a
Scanner
that reads fromos.Stdin
.A
for
loop continues as long asscanner.Scan()
returnstrue
, meaning that input is available.For each line, we print the input using
fmt.Println()
.If an error occurs while reading input, we print it to
stderr
.
Usage examples:
Interactive input: You can type some text and press
Enter
. The program will read the input and print it back.End-of-file (EOF): Press
Ctrl+D
(Linux/Mac) orCtrl+Z
(Windows) to signal the end of input and terminate the program.Redirect input from a file: Run the program with
./program < input.txt
to read from a file instead of typing input manually.Pipe input from another program: Use
echo "Hello, World!" | ./program
to pass the output ofecho
as input to your program.
Enhanced Example: Handling Missing Input and Fallback to a File
A more flexible approach involves checking if any data is available on stdin
. If there’s no input provided, the program can try reading from a file instead. This is useful if your program can be run both interactively or as part of a pipeline.
How it works:
If there’s no input on
stdin
(i.e.,scanner.Scan()
returnsfalse
), the program checks if a filename is provided as a command-line argument.If no filename is provided, it reports an error and exits.
If a filename is provided, it opens the file and reads from it, just like it would from
stdin
.
Usage examples:
No input from stdin: If you don’t type any input, the program will attempt to open the file provided as an argument (e.g.,
./program input.txt
).File input: If the file doesn’t exist or there’s an issue opening it, the program will display an error message.
Advanced Example: Checking if Input is from stdin
or a File
To make the program even more versatile, we can check if the input is coming from stdin
or from a file by using os.Stdin.Stat()
. This approach is useful when you want to detect the source of input programmatically and handle each case differently.
How it works:
We check the mode of
os.Stdin
usingos.Stdin.Stat()
. Ifstdin
is a terminal (a character device), it means there’s no data piped into the program.If
stdin
is not connected to a terminal, the program falls back to reading from a file provided as an argument.This approach allows the program to dynamically handle different input sources:
stdin
for interactive or piped input, and a file for redirected input.
Usage examples:
Interactive or piped input: When
stdin
is connected to a terminal or another program via a pipe, the program will read fromstdin
.File input: When
stdin
is not connected to a terminal (i.e., input is redirected), the program reads from the provided file.
With these examples, you now have a program capable of handling various types of input sources: interactive user input, piped data from other programs, and file input. By leveraging Go’s bufio
package and os.Stdin.Stat()
, you can create highly flexible command-line utilities.