This Bash redirection trick replaces echo …

Bash—and other Linux shells—support powerful command plumbing using features like redirection. You may be used to the most basic type of input redirection but, as is often the case, Linux has more depth than meets the eye.

Input redirection using <

Input redirection is one of the first advanced shell techniques you’re likely to learn. It’s a mechanism that lets you send data to a command from a file, instead of typing it in directly or piping it from another process.

Most programs—cat, for example—will accept a filename as a command-line argument, if it makes sense for them to do so:

cat filename

But others—tr, for example—do not. According to its man page, “The tr utility copies the standard input to the standard output with substitution or deletion of selected characters.” Its basic usage is given as:

tr string1 string2

So there are two typical ways to use tr: by typing input manually on the command line or, more commonly (especially in scripts), by piping input to it:

cat filename | tr string1 string2

This pipeline uses cat to send the contents of a file to standard output but there’s a better way, using input redirection:

tr string1 string2 <filename

Here, the symbol opens the named file with file descriptor 0, standard input, making it available to tr.

Here documents with <<

If you’ve done a bit of shell scripting, or worked with input on the command line, you may have faced a common problem: how to work with multi-line strings. Although you can get by with escape sequences and other approaches, the here document syntax is much more convenient:

#!/bin/bash
cat <<END
This script will carry out an audit of your disk.
Choose an option:
1. Continue
2. Exit
END

The word after the “

This might seem like a very different action—formatting a multi-line string into a single line—but heredocs are a form of input redirection. Note that this example uses cat, not echo. While echo prints whatever arguments you pass to it, cat prints standard input, which is what we’re dealing with here.

You can confirm this by passing a heredoc to echo:

Passing a heredoc to echo, the shell adds a "heredoc>" prefix to each line until the delimiter is entered.

Your shell will use a different prompt to show that it’s expecting multi-line input. Bash displays a “>” while zsh, as in the above screenshot, uses “heredoc>.”

The echo program doesn’t do anything with standard input, so nothing happens in this case. You’ll see the same lack of output if you run the equivalent pipeline:

echo "hello, world" | echo

The cat program, meanwhile, passes input to standard output:

The cat program run with a heredoc prints text from standard input once it is entered.

The mysterious <<< and here strings

Even if you’re a heredoc master, you may be unaware of this final type of input redirection: the here string. It’s actually very similar to the multi-line version, just with a single line instead:

cat <<< "hello, world"

The cat will fail because it’s the equivalent of echo hello, | cat world. Make sure to quote your here string if it contains spaces.

Because this syntax expects a single-line string, there’s no need for a delimiter. Like here documents, the here string can perform variable substitution, so it’s as versatile as you need it to be:

grep -q /usr/bin <<< $PATH

So, what use is it? For a start, this approach is a little more robust and efficient than its equivalent:

echo "hello, world" | echo

When a command is piped, your shell opens a subshell to run it and connects input and output between the two as appropriate. This is an invaluable process most of the time, but for simple cases like passing text from one command to another, it’s not always necessary. Using a here string avoids this overhead.

A here string can act as better documentation too, more accurately reflecting a command’s intent. Rather than misusing a command like echo or cat to supply input, the here string does this job all on its own, and it only does that job so purpose is clear.

For this reason, it’s also convenient to use as a placeholder. For example, this command searches text for a given pattern:

grep "pattern" <<< "some text to search"

While constructing the pattern, you may want to use dummy text to test it with, which the here string lets you do easily. Then, when you’ve finished refining the pattern, you can replace it with input redirection from a file:

grep "pattern" < filename

The overall command is nearly identical, so consistency is maintained and the final command is easier to formulate.

Occasionally, though, the here string is more than a nicety; it’s a necessity. Take the read builtin, for example:

echo 'hello' | read greeting

The intention here is that $greeting ends up with the value hello but that’s not what happens:

A command that pipes a value into read, then outputs the resulting variable, which is still empty.

Because read runs in a subshell, the greeting variable is not available to the calling shell, so the input gets discarded. But, with a here string, everything works as you’d want it to:

The read command with a here string that correctly populates a variable.

Share this post:

Leave a Reply

Your email address will not be published. Required fields are marked *

From the latest gadgets to expert reviews and unbeatable deals — dive into our handpicked content across all things tech.