Handling standard I/O
STDIN (standard in) refers to an input stream that a program can use to read input from a command shell or Terminal. Similarly, STDOUT (standard out) refers to the stream that is used to write the output. STDERR (standard error) is a separate stream to STDOUT that is typically reserved for outputting errors and diagnostic data.
In this recipe, we're going to learn how to handle input with STDIN, write output to STDOUT, and log errors to STDERR.
Getting ready
For this recipe, let's first create a single file named greeting.js
. The program will ask for user input via STDIN, return a greeting via STDOUT, and log an error to STDERR when invalid input is provided. Let's create a directory to work in, too:
$ mkdir interfacing-with-io $ cd interfacing-with-io $ touch greeting.js
Now that we've set up our directory and file, we're ready to move on to the recipe steps.
How to do it
In this recipe, we're going to create a program that can read from STDIN and write to STDIN and STDERR:
- First, we need to tell the program to listen for user input. This can be done by adding the following lines to
greeting.js
:process.stdin.on("data", (data) => { // processing on each data event });
- We can run the file using the following command. Observe that the application does not exit because it is continuing to listen for
process.stdin
data events:$ node greeting.js
- Exit the program using CTRL + C.
- We can now tell the program what it should do each time it detects a data event. Add the following lines below the
// processing on each data event
comment:const name = data.toString().trim().toUpperCase(); process.stdout.write(`Hello ${name}!`);
- You can now type some input in to your program, and it will return the greeting and your name in uppercase:
$ node greeting.js $ Beth Hello BETH
- We can now add a check for whether the input string is empty, and log to STDERR if it is. Change your file to the following:
process.stdin.on("data", (data) => { const name = data.toString().trim().toUpperCase(); if (name !== "") { process.stdout.write(`Hello ${name}!`); } else { process.stderr.write("Input was empty."); } });
Now we've created a program that can read from STDIN and write to STDIN and STDERR.
How it works
process.stdin
, process.stdout
, and process.stderr
are all properties on the process object. A global process object provides the information and control of the Node.js process. For each of the I/O channels, they emit data events for every chunk of data received. In this recipe, we were running the program in interactive mode where each data chunk was determined by the newline character when you hit Enter in your shell. process.stdin.on("data", (data) => {...});
is what listens for these data events. Each data event returns a Buffer object. The Buffer object (typically named data
) returns a binary representation of the input.
const name = data.toString()
is what turns the Buffer object into a string. The trim()
function removes the newline character that denoted the end of each input.
We write to STDOUT and STDERR using the respective properties on the process object (process.stdout.write
, process.stderr.write
).
During the recipe, we also used CTRL +
C to exit the program in the shell. CTRL +
C sends SIGINT
, or signal interrupt, to the Node.js process. For more information about signal events, refer to the Node.js Process API documentation at https://nodejs.org/api/process.html#process_signal_events.
Important note
Console APIs: Under the hood, console.log
and console.err
are using process.stdout
and process.stderr
.
See also
- Chapter 3, Streams, streams, streams