Bash read builtin command

Updated: 12/31/2020 by Computer Hope
read command

On Unix-like operating systems, read is a builtin command of the Bash shell. It reads a line of text from standard input and splits it into words. These words can then be used as the input for other commands.

Description

read reads a single line from standard input, or from the file descriptor fd if the -u option is used (see -u, below).

By default, read considers a newline character as the end of a line, but this can be changed using the -d option.

After reading, the line is split into words according to the value of the special shell variable IFS, the internal field separator. By default, the IFS value is "space, tab, or newline". If read encounters readable characters before encountering an IFS, it considers those characters to be a word. (For more information about the IFS, see word splitting in bash.)

Tip

To preserve white space at the beginning or the end of a line, it's common to specify IFS= (with no value) immediately before the read command. After reading is completed, the IFS returns to its previous value. For more about this, see the examples below.

read assigns the first word it finds to name, the second word to name2, etc. If there are more words than names, all remaining words are assigned to the last name specified. If only a single name is specified, the entire line is assigned to that variable.

If no name is specified, the input is stored in a variable named REPLY.

Syntax

read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars]
     [-p prompt] [-t timeout] [-u fd] [name ...] [name2 ...]

Options

The read builtin command takes the following options:

-a array Store the words in an indexed array named array. Numbering of array elements starts at zero.
-d delim Set the delimiter character to delim. This character signals the end of the line. If -d is not used, the default line delimiter is a newline.
-e Get a line of input from an interactive shell. The user manually inputs characters until the line delimiter is reached.
-i text When used in conjunction with -e (and only if -s is not used), text is inserted as the initial text of the input line. The user is permitted to edit text on the input line.
-n nchars Stop reading after an integer number nchars characters are read, if the line delimiter has not been reached.
-N nchars Ignore the line delimiter. Stop reading only after nchars characters are read, EOF is reached, or read times out (see -t).
-p prompt Print the string prompt, without a newline, before beginning to read.
-r Use "raw input". Specifically, this option causes read to interpret backslashes literally, rather than interpreting them as escape characters.
-s Do not echo keystrokes when read is taking input from the terminal.
-t timeout Time out, and return failure, if a complete line of input is not read within timeout seconds. If the timeout value is zero, read will not read any data, but returns success if input was available to read. If timeout is not specified, the value of the shell variable TMOUT is used instead, if it exists. The value of timeout are fractional numbers, e.g., 3.5.
-u fd Read from the file descriptor fd instead of standard input. The file descriptor should be a small integer. For information about opening a custom file descriptor, see opening file descriptors in bash.

Exit status

The exit status of read is zero unless EOF is encountered, the timeout is exceeded, an error occurs assigning a value to name, or the file descriptor provided to -u is invalid.

If a timeout is exceeded, the exit status is greater than 128.

Examples

while read; do echo "$REPLY"; done

read takes data from the terminal. Type whatever you'd like, and press Enter. The text is echoed on the next line. This loop continues until you press Ctrl+D (EOF) on a new line. Because no variable names were specified, the entire line of text is stored in the variable REPLY.

while read text; do echo "$text"; done

Same as above, using the variable name text.

while read -ep "Type something: " -i "My text is " text; do 
  echo "$text";
done

Provides a prompt, and initial text for user input. The user can erase "My text is ", or leave as part of the input. Typing Ctrl+D on a new line terminates the loop.

echo "Hello, world!" | (read; echo "$REPLY")

Enclosing the read and echo commands in parentheses executes them in a dedicated subshell. This allows the REPLY variable to be accessed by both read and echo. For more information, see bash command execution environments: subshells.

echo "one two three four" | while read word1 word2 word3; do
  echo "word1: $word1"
  echo "word2: $word2"
  echo "word3: $word3"
done

Echo "one two three four" and pipe it to the while loop, where read reads the first word into word1, the second into word2, and everything else into word3. Output:

word1: one
word2: two
word3: three four
echo "one two three four" | while read -a wordarray; do
  echo ${wordarray[1]}
done

Same as above, but assign the words to the elements of an indexed array, wordarray. The numbering starts at zero, so ${wordarray[1]} returns the second word. For more information, see bash arrays. Output:

two
while IFS= read -r -d $'\0' file; do
  echo "$file"
done < <(find . -print0)

The above commands are the "proper" way to use find and read together to process files. It's especially useful when you want to do something to a lot of files that have odd or unusual names. Let's take a look at specific parts of the above example:

while IFS=

IFS= (with nothing after the equals sign) sets the internal field separator to "no value". Spaces, tabs, and newlines are therefore considered part of a word, which preserves white space in the file names.

Note that IFS= comes after while, ensuring that IFS is altered only inside the while loop. For instance, it won't affect find.

read -r

Using the -r option is necessary to preserve any backslashes in the file names.

-d $'\0'

The -d option sets the newline delimiter. Here, we're setting it to the NULL character, ASCII code zero. (An escaped zero enclosed in single quotes, preceded by a dollar sign, is interpreted by bash as NULL. For more information, see: Expanding strings with interpreted escapes in the bash documentation.)

We're using NULL as the line delimiter because Linux file names can contain newlines, so we need to preserve them. (This sounds awful, but yes, it happens.)

However, a NULL can never be part of a Linux file name, so that's a reliable delimiter to use. Luckily, find can use NULL to delimit its results, rather than a newline, if the -print0 option is specified:

< <(find . -print0)

Here, find . -print0 creates a list of every file in and under . (the working directory) and delimit all file names with a NULL. When using -print0, all other arguments and options must precede it, so make sure it's the last part of the command.

Enclosing the find command in <( ... ) performs process substitution: the output of the command can be read like a file. In turn, this output is redirected to the while loop using the first "<".

Every iteration of the while loop, read reads one word (a single file name) and puts that value into the variable file, which we specified as the last argument of the read command.

When there are no more file names to read, read returns false, which triggers the end of the while loop, and the command sequence terminates.

while — Execute a set of actions while a certain condition is true.
find — Find files within a directory hierarchy.