Variable Assignment and Redirection

Along with command words, a simple command can include special variable assignment words as well as redirection operators. These are processed after command word expansion has occurred, just before a command is executed.

Variable Assignment

At the beginning of a command, any words of the form, name=value, are treated as variable assignment words. The name preceding the = is treated as the name of a variable, and the remainder of the word following the = is the value that is assigned, which may be empty (e.g X=). The value undergoes , Parameter Expansion, Command Substitution, and Arithmetic Expansion, but not Field Splitting or Pathname Expansion. The expanded value is assigned to the named variable.

If no command name is present after expanding the command words, the variable assignment affects the current shell environment and becomes a shell variable; otherwise, if a command name is present, the variable assignment affects that particular command only, and is added to that command’s environment variables before execution occurs:

$ x=12              # No command name -> x becomes a shell variable!
$ echo $x
12
$ x=13 printenv x   # x=13 adds "x=13" to the environment of `printenv`; does not modify shell environment
x=13
$ echo $x           # The shell variable 'x' is still 12.
12

Redirection

Recall that every program expects the standard in, out, and error–file descriptors 0, 1, and 2–to already be open when it is executed. Since processes inherit these file descriptors from their parent, any executed commands will read and write to the same locations that the parent shell process is reading and writing from; in the case of an interactive session, this would be the terminal device.

This behavior is often undesirable; the user may not want to interactively provide input to a program, but instead want to pass a file as input. Likewise, a user may want to write output to a file rather than to the terminal. This is accomplished through a shell feature called redirection.

Redirection is a mechanism by which the shell opens new files for reading and/or writing just before executing a command, so that the command inherits the newly opened files rather than the ones the shell itself has open.

File Redirection

There are five redirection operators which can be used to open files with different access modes:

Operator

Access Mode

Creates File if Missing?

Truncates Existing File?

>

Write

Yes

Yes, if noclobber unset[1]

>|

Write

Yes

Yes

<

Read

No

No

<>

Read+Write

Yes

Yes

>>

Append

Yes

No

Each redirection operator takes the word following it as a filename argument, which designates the file to be opened. This filename word undergoes all of the expansion steps that command words go through, except that field splitting is not performed, and pathname expansion may be performed only if it results in a single field (matched filename).

Additionally, an optional io-number can be prepended to the operator to specify the file descriptor number that should be associated with the open file. If the optional io-number is omitted, it is implicitly 1 (stdout) for all operators that begin with ‘>’, and 0 (stdin) for all operators that begin with ‘<’.

For example, the following command runs the cut utility to print the first field (column) of a tab-delimited file called gradebook, and writes the output to a file called roster, using the < and > redirection operators, with their implied io numbers of 0 and 1:

$ cut -f 1 <gradebook >roster

, which is exactly equivalent to:

$ cut -f 1 0<gradebook 1>roster

The redirection operators are processed from left to right, and the operators and their operands are removed from the command words before the command is executed. In the above command, the shell opens the file gradebook on file descriptor number 0 (stdin) for reading. Next, it opens the file roster on file descriptor number 1 (stdout) for writing. Finally, it executes the cut utility with two arguments, -f and 1. The cut utility is unaware that redirection has occurred.

Redirection operators and their operands may appear anywhere in a command, intermixed with regular command words. For example, the above command is equivalent to <gradebook cut -f >roster 1. Conventionally, redirections are placed at the end of a command for readability. Rarely I find them written at the front of a very long command, since a reader may care more about where the i/o occurs rather than the contents of many long arguments to the command in question:

<input_file >output_file 2>err_log a_very_long_command with a lot of arguments...

Duplication

Two additional redirection operators can be used to duplicate existing file descriptors: >& and <&. The filename argument in this case is interpreted as an io number, and the left side is made a copy of the right side. The two operators are essentially interchangeable when provided with an explicit io number (left operand), differing only in that their default io numbers are 1 and 0, respectively, following the rule described above. The < and > versions do not affect read/write/append properties of any of the open files; the duplicated file descriptor retains the access mode of the original.

Here Documents

The last two redirection operators are << and <<-, the here-document operators. These operators interpret their filename arguments, subject to Quote Removal as an end-of-file token. The shell reads lines of input following the current line of input until reaching a line that contains exactly the end-of-file token it encountered earlier. All of the additional lines read up to that point–called the here-document–are treated as the contents of a temporary file that is opened on the specified file descriptor number.

The two operators are the same except that the <<- version discards leading <tab> indentation in the here-document. The contents of the here-document also undergo Tilde Expansion, Parameter Expansion, Command Substitution, and Arithmetic Expansion, if the filename argument does not contain any quoted characters. Otherwise all of the here-document text is treated as literal text.

Examples:

$ cat <<EOF
> Hello!
> My name is Benny Beaver!
> Nice to meet you :)
> EOF
Hello!
My name is Benny Beaver!
Nice to meet you :)
$ cat <<EOF
> Expanded \$HOME: "$HOME"
> EOF
Expanded $HOME: "/home/bennyb"
$ cat <<\EOF  # filename contains a quoted character -- expansion is suppressed
> Expanded \$HOME: "$HOME"
>EOF
Expanded \$HOME: "$HOME"