| 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 |
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:
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:
read -r -p "Name: " name
printf 'Hello, %s\n' "$name"When the user types Alice and presses Enter, the session looks like this:
Name: Alice
Hello, AliceTo reproduce that without typing, supply input with a here-string in the same shell (no pipeline subshell):
read -r name <<< 'Alice'
printf 'Hello, %s\n' "$name"Hello, AliceThe 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=:
read -r name <<< 'Alice Bob Smith'
printf '<%s>\n' "$name"<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:
read line <<< 'line\with\backslashes'
printf 'no -r: %s\n' "$line"no -r: linewithbackslashesThe backslashes disappeared because read interpreted them. Add -r when you want the characters the user typed:
read -r line <<< 'line\with\backslashes'
printf 'with -r: %s\n' "$line"with -r: line\with\backslashesUse 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:
IFS= read -r line <<< ' spaced '
printf '<%s>\n' "$line"< 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:
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:
Continue? [y/N] yes
ans=yesFor yes/no confirmations, branch with case (see Bash case statement for pattern lists):
read -r -p "Continue? [y/N] " ans
case "$ans" in
[yY]|[yY][eE][sS]) echo "confirmed" ;;
*) echo "declined" ;;
esacIf the user types maybe, the default branch runs and prints declined:
Continue? [y/N] maybe
declinedOnly [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):
IFS=, read -r first second age <<< 'Alice,Bob,42'
printf 'first=%s second=%s age=%s\n' "$first" "$second" "$age"first=Alice second=Bob age=42Setting 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.
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:
Password:
length=9The 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:
unset pwunset 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):
read -r line < /dev/null
printf 'exit=%s\n' "$?"exit=1A real timeout needs a source that stays open without sending a line. Process substitution with sleep blocks long enough for -t to fire:
read -r -t 1 line < <(sleep 2)
printf 'exit=%s\n' "$?"exit=142On 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:
read -r -t 2 line <<< 'quick'
printf 'exit=%s line=%s\n' "$?" "$line"exit=0 line=quickBranch on read directly when a timeout or EOF should trigger a fallback — Bash if else covers testing command status without saving $? first:
if read -r -t 5 name; then
printf 'Hello, %s\n' "$name"
else
name=guest
fiThe 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 mostNUMcharacters; may return early on delimiter or EOF-N NUM— read exactlyNUMcharacters when enough input exists
Compare one-character reads from the string abc on one input stream:
printf 'abc\n' | { read -r -n 1 c; printf 'char=%s\n' "$c"; read -r -n 1 rest; printf 'rest=%s\n' "$rest"; }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:
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}"; }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:
{ read -r -d : part; read -r rest; } <<< 'yes:ok'
printf 'part=%s rest=%s\n' "$part" "$rest"Sample output:
part=yes rest=okThe 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.
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:
Filename: backup.tar.gz
filename=backup.tar.gzThat 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):
read -r -a words <<< 'one two three'
declare -p wordsdeclare -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:
read -r <<< 'stored in reply'
printf 'REPLY=%s\n' "$REPLY"REPLY=stored in replyCopy 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:
read -r line <<< ''
printf 'exit=%s val=%s\n' "$?" "$line"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:
exec 3< <(printf 'fd line\n')
read -r -u 3 line
printf 'line=%s\n' "$line"
exec 3<&-line=fd lineread -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:
printf 'Alice\n' | read -r name
printf '<%s>\n' "$name"<>The pipe fed read, but name in your current shell stayed empty.
Read in the current shell instead:
read -r name <<< 'Alice'
printf '<%s>\n' "$name"<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:
while IFS= read -r line || [[ -n $line ]]; do
printf '%s\n' "$line"
done < input.txtThe || [[ -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
- Bash manual —
readbuiltin — options, exit status, andREPLY - Bash manual — Pipelines — subshell behavior in pipelines
- Bash manual — Word Splitting — how
IFSaffectsread
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.

