Bash read Command: Get User Input

Tested on RHEL 10.2 (Coughlan)
Package bash 5.2.26-6.el10
Applies to Linux distributions with Bash installed
Privilege Normal user; no root required for the examples
Scope Bash read builtin for interactive and redirected input: one or more variables, read -r, prompts, IFS splitting, hidden input, timeouts, character and delimiter reads, arrays, REPLY, exit status, a brief -u file-descriptor example, and the usual while IFS= read -r file-line pattern. Detailed file-by-file reading patterns are covered separately.
Related guides Bash shell script tutorial
Shell scripting course hub
Short on time?Beta Get the gist of this 14-minute read in seconds, then ask follow-up questions

In this article, you'll learn how the Bash read builtin stores input in variables and how its options control prompts, splitting, hidden input, timeouts, arrays, and other input sources.

By default, read waits for a newline, usually when the user presses Enter. Options can instead stop at a character count, custom delimiter, timeout, or EOF. It stores the answer in Bash variables, so you can validate or use the values immediately afterward.


Bash read Syntax and Quick Reference

read is a shell builtin, not an external program. It reads a line (or part of a line) from standard input and stores the result in one or more variables.

Option Effect
-r Do not treat backslash as an escape character
-p PROMPT Display a prompt before reading, when input comes from a terminal
-s Do not echo typed characters to the terminal (when input is a terminal)
-t SECONDS Fail if the required input is not completed within SECONDS
-e Use Readline when reading from a terminal
-i TEXT Place TEXT in the Readline editing buffer (with -e)
-n NUM Read at most NUM characters (may leave data on the line)
-N NUM Read exactly NUM characters when possible
-d CHAR Use CHAR as end-of-input delimiter instead of newline
-a ARRAY Store words into indexed array ARRAY
-u FD Read from file descriptor FD instead of stdin

Basic form:

text
read [options] [name ...]

If you omit variable names, Bash stores the line in REPLY. On success, read returns exit status 0. On EOF it returns a non-zero status (often 1 on an empty stream). On -t timeout it returns a status greater than 128; the exact value is system-dependent and should not be hard-coded.


Read Input into One Variable

In an interactive script, read -r name waits for the user to type a line and press Enter:

text
read -r -p "Name: " name
printf 'Hello, %s\n' "$name"

When the user types Alice and presses Enter, the session looks like this:

text
Name: Alice
Hello, Alice

To reproduce that without typing, supply input with a here-string in the same shell (no pipeline subshell):

bash
read -r name <<< 'Alice'
printf 'Hello, %s\n' "$name"
output
Hello, Alice

The here-string only feeds test input; it does not create a pipeline subshell, so name stays set in your current shell.

read uses IFS when assigning input to variables. If you provide only one variable name, that variable receives the remaining input rather than discarding later words. With multiple variable names, Bash assigns fields from left to right and puts any remaining fields into the last variable.

With one variable, internal words stay together instead of being discarded. Leading and trailing IFS whitespace can still be removed unless you use IFS=:

bash
read -r name <<< 'Alice Bob Smith'
printf '<%s>\n' "$name"
output
<Alice Bob Smith>

Why read -r Is Usually the Right Default

Without -r, Bash treats backslash as an escape inside the line it reads. A path or regex the user types literally can change shape:

bash
read line <<< 'line\with\backslashes'
printf 'no -r: %s\n' "$line"
output
no -r: linewithbackslashes

The backslashes disappeared because read interpreted them. Add -r when you want the characters the user typed:

bash
read -r line <<< 'line\with\backslashes'
printf 'with -r: %s\n' "$line"
output
with -r: line\with\backslashes

Use read -r for file paths, URLs, regular expressions, and any literal text. Omit -r only when you deliberately want read to treat backslashes specially.

Preserve leading and trailing whitespace with IFS=

-r stops backslash escapes; it does not stop read from trimming leading or trailing characters that appear in IFS. To keep edge whitespace on the line, clear IFS for that read call:

bash
IFS= read -r line <<< '  spaced  '
printf '<%s>\n' "$line"
output
<  spaced  >

-r preserves backslashes; IFS= prevents the usual leading and trailing IFS whitespace trimming.


Prompt for Input with -p

-p prints a prompt string before read waits for input, but only when input comes from a terminal. For prompt and output formatting outside read -p, see Bash echo vs printf. If stdin is a pipe or a here-string, Bash does not display the prompt.

In a script:

text
read -r -p "Continue? [y/N] " ans
printf 'ans=%s\n' "$ans"

When the user types yes, the terminal shows the prompt and the stored answer:

text
Continue? [y/N] yes
ans=yes

For yes/no confirmations, branch with case (see Bash case statement for pattern lists):

text
read -r -p "Continue? [y/N] " ans
case "$ans" in
  [yY]|[yY][eE][sS]) echo "confirmed" ;;
  *) echo "declined" ;;
esac

If the user types maybe, the default branch runs and prints declined:

text
Continue? [y/N] maybe
declined

Only [yY] and full yes spellings count as confirmation; an empty line also declines because it does not match the yes patterns.

Automated tests that pipe input into read will not show the -p prompt even though the variable is still filled. This is expected because -p only displays its prompt when read receives input from a terminal.


Read Multiple Values and Control Splitting with IFS

read can fill several variables in one call. Bash splits the input line using IFS (internal field separator):

bash
IFS=, read -r first second age <<< 'Alice,Bob,42'
printf 'first=%s second=%s age=%s\n' "$first" "$second" "$age"
output
first=Alice second=Bob age=42

Setting IFS=, only for that command limits splitting to commas without changing IFS for the rest of the script. Extra fields go into the last variable when you name fewer variables than fields; with more variables than fields, the extras stay empty.

Prefer setting IFS on the read command itself, as above, so you do not accidentally change field splitting elsewhere in the script.


Read Passwords and Hidden Input with -s

-s turns off terminal echo while the user types. Like -p, it applies when input comes from a terminal—not when stdin is a pipe or here-string.

text
read -r -s -p "Password: " pw
printf '\nlength=%d\n' "${#pw}"

At the terminal the password does not echo; only the prompt line and the length print afterward:

text
Password:
length=9

The password characters are not shown. printf '\n' moves the following output to the next line after read returns.

-s only disables terminal echo while the user types. The value still exists as a normal shell variable and can be exposed if your script prints, logs, or passes it insecurely to another command.

Remove the variable when you no longer need it:

text
unset pw

unset removes the name from Bash's variable table, which helps prevent accidental reuse. It is not a secure-memory wipe. read -s controls terminal echo; it does not provide secure storage for the value.


Set a Timeout with -t

-t SECONDS makes read fail when the required input is not completed in time, even if some characters were already typed. Timeout and EOF are both failures, but they use different exit statuses.

EOF on an empty stream returns a non-zero status (here, 1):

bash
read -r line < /dev/null
printf 'exit=%s\n' "$?"
output
exit=1

A real timeout needs a source that stays open without sending a line. Process substitution with sleep blocks long enough for -t to fire:

bash
read -r -t 1 line < <(sleep 2)
printf 'exit=%s\n' "$?"
output
exit=142

On this test system, the command returned exit status 142. Bash guarantees that a timeout returns a status greater than 128; the exact value should not be hard-coded. That is not the same code as EOF. Bash may also leave partial input in the variable if a timeout happens after some characters were read.

When input arrives in time, read succeeds:

bash
read -r -t 2 line <<< 'quick'
printf 'exit=%s line=%s\n' "$?" "$line"
output
exit=0 line=quick

Branch on read directly when a timeout or EOF should trigger a fallback — Bash if else covers testing command status without saving $? first:

text
if read -r -t 5 name; then
    printf 'Hello, %s\n' "$name"
else
    name=guest
fi

The else branch runs on timeout, EOF, or any other read failure. If you need to distinguish timeout from another failure, save $? immediately after read before another command overwrites it.


Read Characters and Delimiters (-n, -N, -d)

-n and -N

Both limit how many characters read consumes, but they behave differently:

  • -n NUM — read at most NUM characters; may return early on delimiter or EOF
  • -N NUM — read exactly NUM characters when enough input exists

Compare one-character reads from the string abc on one input stream:

bash
printf 'abc\n' | { read -r -n 1 c; printf 'char=%s\n' "$c"; read -r -n 1 rest; printf 'rest=%s\n' "$rest"; }
output
char=a
rest=b

-n 1 took a; the next read continued on the same line.

With -N, Bash pulls a fixed width from the input stream:

bash
printf 'abc\n' | { read -r -N 1 c; printf 'char=%s (len=%s)\n' "$c" "${#c}"; read -r -N 2 rest; printf 'rest=%s (len=%s)\n' "$rest" "${#rest}"; }
output
char=a (len=1)
rest=bc (len=2)

When reading interactively from a terminal, -n 1 is useful for single-key choices such as y, n, or q without waiting for Enter. Use -N when you need to consume an exact number of characters from the input stream.

-d

-d CHAR sets the end-of-input character instead of newline. The delimiter is not stored in the variable:

bash
{ read -r -d : part; read -r rest; } <<< 'yes:ok'
printf 'part=%s rest=%s\n' "$part" "$rest"

Sample output:

output
part=yes rest=ok

The first read stopped at :. The second read picked up the remainder of the line.


Pre-fill Input with -e and -i

-e enables Readline editing when read reads from a terminal. -i TEXT places initial text in the editing buffer so the user can accept or edit it before pressing Enter.

text
read -r -e -i "backup.tar.gz" -p "Filename: " filename
printf 'filename=%s\n' "$filename"

If the user accepts the pre-filled name with Enter, the session looks like this:

text
Filename: backup.tar.gz
filename=backup.tar.gz

That differs from a default applied after an empty answer, such as ${filename:-backup.tar.gz}: with -i, the default text is already on the line before Enter; with :-, an empty read leaves the variable empty until your script supplies a fallback.


Read into an Array with -a

-a assigns whitespace-separated words to an indexed array (default IFS splitting):

bash
read -r -a words <<< 'one two three'
declare -p words
output
declare -a words=([0]="one" [1]="two" [2]="three")

For CSV-like lines, combine IFS with read -r -a in the same way as multiple scalar variables. Full array programming (append, unset elements, associative arrays) is a separate topic; here read is only the input step.


REPLY and read Exit Status

Success, EOF, and timeout map to different exit statuses — Bash exit codes explains how $? behaves after read and other builtins.

When you call read with no variable names, the line lands in REPLY:

bash
read -r <<< 'stored in reply'
printf 'REPLY=%s\n' "$REPLY"
output
REPLY=stored in reply

Copy REPLY into a named variable promptly if later read calls would overwrite it.

Exit status summary:

Condition read exit status
Line read successfully (including an empty line) 0
EOF before delimiter Non-zero (often 1 on an empty stream)
-t timeout Greater than 128 (host-specific; not the same as EOF)

A successful read of an empty line still returns 0:

bash
read -r line <<< ''
printf 'exit=%s val=%s\n' "$?" "$line"
output
exit=0 val=

Test with if read -r line; then … else … fi so the script handles EOF or timeout explicitly. Save $? right after read when you need to tell timeout apart from other failures.

Read from a file descriptor with -u

-u FD reads from an open file descriptor instead of stdin. Open the descriptor first, then pass it to read:

bash
exec 3< <(printf 'fd line\n')
read -r -u 3 line
printf 'line=%s\n' "$line"
exec 3<&-
output
line=fd line

read -u lets you read from a specific open file descriptor without replacing standard input. exec 3<&- closes the descriptor when finished.


Why Piping into read Can Lose the Variable

By default, read on the right side of a pipeline runs in a subshell, so variables it sets are normally lost when that subshell exits:

bash
printf 'Alice\n' | read -r name
printf '<%s>\n' "$name"
output
<>

The pipe fed read, but name in your current shell stayed empty.

Read in the current shell instead:

bash
read -r name <<< 'Alice'
printf '<%s>\n' "$name"
output
<Alice>

Here-strings, redirection (read -r name < file), and read with no pipe keep the assignment in the shell you are scripting in. Bash has exceptions such as lastpipe, but scripts should not depend on that setting unless they explicitly enable and control it. See the Bash manual on pipelines for when subshells apply.


When to Use read vs Script Arguments

Use read Use script arguments ($1, $2, …)
Interactive questions, confirmations, and menus Automation, cron jobs, and CI/CD pipelines
Hidden input with -s Reusable command-line tools with flags and operands
Prompts when no arguments were passed Scripts called from other scripts with fixed parameters

Many installers combine both: accept -f /path/to/config for automation, then read -r -p "Continue? " ans when a human runs the script with no flags.


Use read to Process File Lines

read also works with redirected file input inside a Bash while loop:

text
while IFS= read -r line || [[ -n $line ]]; do
    printf '%s\n' "$line"
done < input.txt

The || [[ -n $line ]] condition also processes a final line that does not end with a newline. IFS= preserves leading and trailing whitespace; -r preserves backslashes. Process substitution, reading multiple fields per line, and other file-loop edge cases are in read a file line by line in Bash.


Common Mistakes

Symptom Likely cause Fix
Backslashes or escapes wrong in user paths read without -r Default to read -r for literal text
Variable empty after echo value | read var Pipeline subshell Use here-string, redirection, or read without a pipe
Variable is empty after pressing Enter User submitted an empty line Validate with [[ -n $var ]], apply a default, or prompt again
read returns failure EOF, timeout, assignment error, or invalid FD Check exit status; timeout status is greater than 128
Script hangs forever No timeout on interactive step Add read -r -t N and handle failure
Password appears in output or logs Script printed or passed the value elsewhere -s only hides terminal typing; avoid logging or passing secrets as command-line arguments
Extra words in one scalar variable One name keeps internal words together Name more variables, read -a, or set IFS to split
-p or -s seems to do nothing in tests Stdin is not a terminal Test interactively; pipes and here-strings skip prompt and silent echo

Summary

The Bash read builtin is how scripts wait for typed input: one variable, several fields, or an array. read -r should be your default so backslashes stay literal; pair it with IFS= when you need leading or trailing whitespace preserved.

-p and -s affect the terminal only—prompts and hidden echo do not apply when stdin is a pipe. For passwords, -s hides typing but the value is still a normal variable; unset helps avoid reuse but is not a secure wipe.

-t timeouts return a status greater than 128, which is not the same as EOF. Check whether read succeeded, and inspect its status when you need to distinguish timeout from EOF or another failure. REPLY holds the line when you omit a variable name. Avoid echo value | read var in the parent shell; use a here-string or redirection so the assignment survives.

Use read for interactive flows and script arguments for automation. Validate user input with case, if, or tests before using it in later commands.


References


Frequently Asked Questions

Why use read -r?

Without -r, Bash treats backslash as an escape inside the input line. Paths, URLs, and regexes the user types literally can change. read -r keeps backslashes literal. Add IFS= on the same line when you also need to preserve leading or trailing whitespace.

How do I read a password?

Use read -r -s -p "Password: " var at an interactive terminal. -s turns off echo while typing. Add printf '\n' afterward so the next output starts on a new line. Unset the variable when finished; -s does not encrypt or securely store the value.

How do I set a timeout?

Add -t SECONDS, for example read -r -t 5 name. If no input arrives in time, read fails with an exit status greater than 128 (not the same as EOF). Use an if read …; then … else … fi block to handle silence, and save $? immediately after read when you need to separate timeout from other failures.

What is REPLY?

REPLY is the default variable when read has no name arguments. A lone read -r stores the line in REPLY. Copy it to a named variable before another read overwrites it.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)