36.4 < and <<: Input Redirection and Here Documents
Right, let’s talk about getting data into your commands. You already know that > is for sending output somewhere else, like a file. Well, < is its less-famous but equally brilliant cousin for pulling input from somewhere else. It’s the difference between a command shouting into a megaphone and one listening through a stethoscope.
Think of most commands as lazy teenagers. They’ll just sit there, waiting for you to type input directly into them (from stdin, the standard input stream). But with <, you can pre-record your lecture and play it right into their ears. You’re redirecting a file to be the command’s input, saving you the tedious work of typing it all out manually.
Here’s the classic, almost-too-simple example. You have a file, names.txt, and you want to sort it.
$ cat names.txt
Zaphod
Arthur
Ford
Trillian
# The boring, manual way (you type the names, then hit Ctrl+D)
$ sort
Zaphod
Arthur
Ford
Trillian
# [Ctrl+D]
Arthur
Ford
Trillian
Zaphod
# The brilliant, I-have-better-things-to-do way
$ sort < names.txt
Arthur
Ford
Trillian
Zaphod
The sort command never knew the difference. It just happily accepted the contents of names.txt as if you’d typed them yourself. This is incredibly useful for scripts or any time you’re dealing with pre-existing data.
When < is a Life Saver (and When It’s Overkill)
The real power of < reveals itself with commands that are stubbornly interactive. Take mysql command-line client, for instance. Without redirection, it waits for you to type SQL commands. Yawn. But with <, you can feed it an entire SQL file.
# This will execute every SQL statement in my_database_setup.sql
$ mysql -u user -p database_name < my_database_setup.sql
This is a scriptable, repeatable, and frankly professional way to handle database operations. Now, a word of caution: you can often accomplish the same thing by just passing the filename as an argument (mysql ... database_name < my_database_setup.sql). Many commands, like sort, cat, or grep, are designed to take a filename as an argument. Using < in those cases is like using a rocket launcher to open a door—it works, but it’s redundant. Use < when the command doesn’t have a built-in way to read from a file and expects input only from stdin.
The << Operator: The Here Document
Now, let’s get weird and wonderful with <<, the “Here Document” operator. This is how you tell the shell, “Hey, the input isn’t in a file over there; it’s right here, in this script.”
You give it a “delimiter” word. The shell will read every following line as the input until it encounters a line that contains only that delimiter. It’s perfect for embedding a block of text directly into your script without needing a separate file.
$ cat > new_file.txt << EOF
> This is line one.
> This is line two.
> Variables like $HOME will be expanded.
> EOF
The shell created new_file.txt with those three lines. Notice how it conveniently showed you a secondary prompt > until you closed it with EOF. The choice of EOF (End Of File) is a convention, but you can use any word you like. Just make sure the closing delimiter matches exactly and sits alone on its line.
Quoting the Delimiter: Controlling Expansion
Here’s where designers made a choice you need to understand. By default, the shell performs variable and command substitution within your Here Document. Look at the previous example: the $HOME was expanded to your home directory path. Sometimes that’s what you want. Often, it’s a disaster.
To avoid this, quote the delimiter. Any kind of quoting will do: << 'EOF', << "EOF", or even << \EOF. This tells the shell to treat the entire block of text as literal, inert data.
# With expansion (the default)
$ cat << EOF
> My path is $PATH
> EOF
My path is /usr/local/bin:/usr/bin:/bin
# Without expansion (what you usually want)
$ cat << 'EOF'
> My path is $PATH
> EOF
My path is $PATH
This is a classic pitfall. You write a SQL script with $USER in it, intending for your database to see the literal dollar sign, but the shell happily expands it to your username before the database ever gets a chance to see it. Always quote your Here Document delimiter unless you have a specific reason not to. It’ll save you hours of debugging.
<<<: The Here String
As a bonus round, let’s look at its younger sibling, <<< (the “Here String”). It’s for when your input isn’t a multi-line document but just a single string. It’s like a echo | but cleaner.
# Need to pass a single string to grep? This is elegant.
$ grep "hello" <<< "This string contains the word hello"
This string contains the word hello
It’s a niche tool, but when you need it, it feels incredibly slick. You’re basically creating a temporary, one-line input stream on the fly.