The read command is a Bash shell builtin that captures user input and assigns it to variables, making it essential for interactive scripts. Common use cases include prompting for usernames and passwords, parsing CSV files line by line, implementing confirmation dialogs, and building menu-driven interfaces. By the end of this guide, you will be able to capture single and multiple inputs, handle sensitive data securely, implement timeouts, process files efficiently, and troubleshoot common pitfalls.
This guide covers read syntax, practical examples with expected output, shell compatibility notes, and troubleshooting patterns. If you are building more complex automation workflows, you may also want to explore the bash wait command for process synchronization in your scripts.
The examples in this guide use Bash-specific features. Options like
-p(prompt),-s(silent),-t(timeout),-a(array),-N(exact characters), and-d(delimiter) are not part of POSIX and will not work indash,sh, or BusyBox environments. If your script must run in/bin/sh, useprintffor prompts and avoid Bash-only options.
Understand the read Command
If you are new to read, think of it as a pause button that waits for someone to answer your script before it keeps going. The read command captures a line of input from standard input (stdin) and assigns it to one or more variables. By default, read waits for user input and stores it in a variable, making it essential for interactive scripts.
Basic Syntax
read [options] variable_name
readis the command that captures user input.[options]allows additional behavior modifications.variable_namestores the input provided by the user. If you skip the variable,readstores the input in the built-in$REPLYvariable.
Common read Command Options
The following table summarizes the most frequently used options:
| Task | Options | What They Do |
|---|---|---|
| Prompt users interactively | -p "prompt" | Displays custom text before read waits for input (Bash only). |
| Capture sensitive responses | -s | Hides the characters a user types so passwords stay private (Bash only). |
| Enforce timeouts | -t seconds | Stops waiting after the specified number of seconds and returns control to the script (Bash only). |
| Accept short answers | -n chars | Reads up to the given number of characters or stops when it encounters a delimiter such as newline (Bash only). |
| Require fixed-length input | -N chars | Collects exactly the number of characters you request and ignores delimiters like newline (Bash only). |
| Preserve literal text | -r | Keeps backslashes and other escape characters untouched (POSIX compatible). |
| Store multiple values | -a array | Saves each word into consecutive positions of an array variable (Bash only). |
| Stop on custom delimiters | -d delim | Treats the specified character as the end of input instead of newline (Bash only). |
Example: Capturing a Simple User Input
At its simplest, read waits for someone to type a response before the script continues. This snippet shows the bare minimum needed to collect one answer and react immediately.
echo "What is your name?"
read user_name
echo "Hello, $user_name!"
When you run this script and type Alice, the output looks like this:
What is your name? Alice Hello, Alice!
How the Basic read Prompt Works
- The script displays a prompt asking for the user’s name.
- The
readcommand captures the input and stores it in theuser_namevariable. - The script then outputs a personalized greeting.
This example demonstrates the most basic use of read, but it can be expanded to handle more complex scenarios.
Use the Default $REPLY Variable
When you use read without specifying a variable name, the command automatically stores the input in the built-in $REPLY variable:
read
echo "You entered: $REPLY"
This behavior is useful in quick scripts or when you want a consistent variable name across different sections of your code. Remember to unset or overwrite REPLY after you are finished with it so the value does not leak into later commands.
Capturing Multiple Inputs in a Single Command
Beyond capturing single values, the read command can store multiple inputs by specifying multiple variable names. When a user provides space-separated values, read assigns each value to a corresponding variable.
Example: Reading Multiple Values
echo "Enter your first and last name:"
read first_name last_name
echo "First Name: $first_name"
echo "Last Name: $last_name"
How read Splits Multiple Values
- If a user enters
John Doe,readstores the first value (John) infirst_nameand places the second value (Doe) inlast_name. - If the user enters more words, the last variable (
last_name) captures the remaining values.
Prompting for Database Credentials
System administration scripts use read to collect user credentials or configuration details in a structured manner:
echo "Enter database credentials:"
read db_user db_password
echo "Connecting to database as $db_user..."
This method assigns user input to the correct parameters and avoids the need for complex parsing.
Storing Input in Arrays for Dynamic Data
When the number of input fields varies or you need to process multiple values efficiently, the -a option allows you to store input directly into an array.
Example: Reading Input into an Array
read -a colors -p "Enter your favorite colors: "
echo "First color: ${colors[0]}"
echo "Second color: ${colors[1]}"
echo "All colors: ${colors[@]}"
How the -a Option Builds Arrays
- The
-aoption tellsreadto store each space-separated word as an array element. -pprints a prompt so users know what data to enter before the command waits for input.- You can access individual elements using
${array[index]}notation. - Use
${array[@]}to reference all array elements at once.
Reusing Arrays for Server Checks
Arrays are particularly useful when processing command output or building dynamic configurations. Using a herestring keeps read in the current shell, so the array stays populated for the loop that follows. If you pipe into the command instead, the loop runs in a subshell and discards the array once the subshell exits, so stick with a herestring or input redirection when you need to reuse the data.
read -a servers <<< "server1 server2 server3"
for server in "${servers[@]}"; do
echo "Pinging $server..."
ping -c 1 "$server" > /dev/null 2>&1 && echo "$server is up"
done
Example: Parsing Data with Herestrings
read -r first last <<< "John Doe"
echo "First: $first, Last: $last"
Expected output:
First: John, Last: Doe
This technique is especially useful when combined with command substitution to parse command output directly:
IFS="@" read -r user host <<< "$(whoami)@$(hostname)"
echo "Current user: $user on host: $host"
By setting IFS to @ for the duration of the command, the output splits cleanly into the username and hostname fields instead of treating the entire string as one value.
Using a Custom Delimiter for Input Splitting
By default, read splits input at whitespace such as spaces, tabs, and newline characters because the Internal Field Separator (IFS) includes those values. When dealing with structured data like CSV files or other delimited formats, you often need a different delimiter, and adjusting IFS changes how read separates text.
Example: Reading CSV Data with a Custom Delimiter
The IFS variable controls how read splits words within a line. Setting it to a comma before read tells the command to split on commas instead of whitespace:
IFS="," read -r name age city <<< "Alice,30,New York"
echo "Name: $name"
echo "Age: $age"
echo "City: $city"
Expected output:
Name: Alice Age: 30 City: New York
Why IFS=”,” Splits CSV Fields
IFS=","sets the delimiter to a comma.- The input
Alice,30,New Yorksplits into three variables:name,age, andcity. -rprevents backslashes from acting as escape characters.- This technique is useful when processing structured data formats.
Processing CSV Rows from a File
Parsing CSV files using read is a common task in Linux scripting. A more practical implementation would involve reading a CSV file line by line:
while IFS="," read -r name age city; do
echo "Processing user: $name, Age: $age, City: $city"
done < users.csv
This script efficiently processes structured data, making it useful for data migration, reporting, and automation. For more advanced text processing needs beyond simple field splitting, consider using the sed command, which offers powerful pattern matching and transformation capabilities.
Using -d for Line Delimiters
While IFS controls how words split within a line, the -d option changes what marks the end of a line. By default, read stops at a newline, but you can specify a different character:
read -d ";" data <<< "status=ready;pending"
echo "Read until semicolon: $data"
Expected output:
Read until semicolon: status=ready
This is useful when processing data that uses non-standard line terminators, such as Windows-formatted files or custom data streams. The herestring keeps the command in the current shell, and the command consumes the semicolon delimiter so the stored value excludes it.
Reading Password Input Securely
When handling sensitive data such as passwords or API keys, displaying user input on the terminal is not ideal. The -s option ensures that input remains hidden from view.
Example: Secure Password Input
read -s -p "Enter your password: " password
printf "\nPassword saved.\n"
Why -s Protects Password Input
-shides the password input so the terminal never displays the characters.-pprovides a user-friendly prompt message.
Securely Passing Passwords to MySQL
Authentication scripts use this method:
read -s -p "Enter your MySQL password: " mysql_pass
printf "\n"
mysql --defaults-extra-file=<(printf "[client]\nuser=root\npassword=%s\n" "$mysql_pass") -e "SHOW DATABASES;"
unset mysql_pass
Using a temporary client credentials file keeps the password out of the process list. Always clear the variable afterward to reduce exposure in memory.
Implementing a Timeout for User Input
In automated or unattended environments, there are scenarios where a script should proceed if the user does not enter input within a given time. The -t option allows setting a timeout to prevent indefinite waiting.
Example: Setting a Timeout for Input
read -t 5 -p "Enter your choice (default is 'no action'): " choice
choice=${choice:-"no action"}
echo "You selected: $choice"
How the Timeout Logic Works
-t 5sets a timeout of 5 seconds.- If the user provides nothing, the script assigns a default value (
no action).
Using Timeouts in Unattended Scripts
Unattended scripts commonly rely on timeouts:
echo "Press any key to continue, or wait 10 seconds..."
read -t 10 -n 1 key || echo "Timeout reached, proceeding..."
This approach prevents indefinite waiting and ensures script execution continues smoothly.
Reading a Single Character Input
For simple yes/no confirmations or menu selections, reading a single character is more efficient than waiting for full-text input. The -n option limits the number of characters captured.
Example: Capturing a Single Keypress
read -n 1 -p "Press Y to continue: " key
printf "\nYou pressed: %s\n" "$key"
Why -n 1 Captures a Single Keypress
-n 1ensures that only one character is read.-pdisplays a message before capturing input.
Quick Confirmations Such as Reboots
Scripts that require quick confirmations use this method:
read -n 1 -p "Reboot now? (Y/N): " confirm
[[ $confirm == [Yy] ]] && sudo reboot
This technique improves user experience by reducing unnecessary keystrokes.
Reading Exactly N Characters with -N
The -N option (uppercase) reads exactly the specified number of characters, ignoring any delimiters including newlines:
read -N 5 -p "Enter exactly 5 characters: " code
printf "\nCode entered: %s\n" "$code"
Unlike -n, pressing Enter does not terminate the command when using -N. The command waits until you enter exactly five characters. This is useful for fixed-length codes, PINs, or formatted input validation.
Understanding Return Codes and Error Handling
The read command returns different exit codes depending on whether the operation succeeds or fails. Proper error handling ensures your scripts behave correctly in all scenarios.
Exit Status Values
- 0: Successfully read input
- Greater than 0: Error occurred (timeout, EOF, invalid variable name, etc.)
Example: Checking read Success
if read -t 5 -p "Enter your choice: " choice; then
echo "You selected: $choice"
else
echo "No input received or timeout occurred"
fi
This pattern is essential for building robust scripts that handle user interaction gracefully, especially when combined with timeouts or when reading from pipes that might close unexpectedly.
Reading Input from a File
Beyond interactive user input, scripters often use the read command in a loop to process files line by line. This approach is essential for automation tasks that need to parse configuration files, process logs, or handle batch data.
Example: Reading a File Line by Line
while IFS= read -r line; do
echo "Processing: $line"
done < file.txt
Why IFS= read -r Preserves File Content
- The
whileloop iterates over each line infile.txt. IFS= read -r linecaptures the content of each line without trimming spaces or interpreting backslashes.
Watching Logs for Error Strings
Automating log file processing is another practical scenario where read proves invaluable. The loop below follows /var/log/syslog continuously so new entries trigger your automation immediately:
while IFS= read -r log_entry; do
echo "Checking log: $log_entry"
[[ $log_entry == *"ERROR"* ]] && echo "Alert: An error was found!"
done < <(tail -f /var/log/syslog)
Using an empty IFS value and the -r flag preserves each log entry exactly as written while process substitution keeps the loop in the current shell. To refine your log analysis further, you can combine read with the grep command to filter specific patterns before processing each line.
Troubleshooting Common Issues
Even experienced users occasionally encounter challenges when working with the read command. The following sections address the most common issues with error messages and solutions.
Illegal Option Errors in Non-Bash Shells
If you run a script using sh or dash and see errors like this:
read: Illegal option -s read: Illegal option -t read: Illegal option -p
The script is running in a POSIX shell that does not support Bash-specific options. Fix this by ensuring your script runs with Bash:
#!/bin/bash
# Use /bin/bash, not /bin/sh, for Bash features
Alternatively, check which shell is executing your script:
echo $0
echo $BASH_VERSION
If $BASH_VERSION is empty, you are not running Bash.
Variable Empty After Piping to read
A common mistake is piping data into read and then finding the variable is empty:
# This does NOT work as expected
echo "hello" | read myvar
echo "Value: $myvar"
Value:
The variable is empty because the pipe creates a subshell for the read command, and the variable is discarded when the subshell exits. Use a herestring or process substitution instead:
# Correct: use herestring
read myvar <<< "hello"
echo "Value: $myvar"
Value: hello
Unexpected Behavior with Whitespace
If leading or trailing spaces disappear from input, the default IFS value (space, tab, newline) is stripping them. Set IFS to an empty value and use the -r flag to preserve raw input:
# Without IFS=, leading spaces are stripped
read line <<< " hello world "
echo "[$line]"
[hello world]
# With IFS=, whitespace is preserved
IFS= read -r line <<< " hello world "
echo "[$line]"
[ hello world ]
When you later reference the variable, always wrap it in double quotes (e.g., "$line") to prevent word splitting and preserve the exact content.
Backslashes Disappearing from Input
Without the -r flag, read interprets backslashes as escape characters:
# Without -r, backslashes are interpreted
read path <<< "C:\Users\test"
echo "$path"
C:Userstest
# With -r, backslashes are preserved
read -r path <<< "C:\Users\test"
echo "$path"
C:\Users\test
Always use -r unless you specifically need backslash interpretation.
Read in a Loop Consumes stdin
When using read inside a while loop that also reads from stdin, the loop may exit unexpectedly or consume input meant for the user. Use a different file descriptor to separate file input from user prompts:
# Read file on fd 3, keep stdin free for user input
while IFS= read -r line <&3; do
echo "Processing: $line"
read -p "Continue? (y/n): " answer
[[ $answer != "y" ]] && break
done 3< file.txt
This technique keeps file data on file descriptor 3 while leaving stdin available for interactive prompts.
Best Practices Summary
These patterns will help you write reliable Bash scripts using the read command:
- Always use
-rto prevent backslash interpretation unless you specifically need escape sequences. - Set
IFS=when reading files line by line to preserve leading and trailing whitespace. - Quote variables (
"$var") when using them to prevent word splitting. - Use herestrings (
<<<) or process substitution (< <(command)) instead of pipes to keep variables in the current shell. - Combine
-swith-pfor password prompts, and remember to print a newline afterward since-ssuppresses the enter key echo. - Set timeouts with
-tand provide default values using parameter expansion (${var:-default}) for unattended scripts. - Check the exit status of
readwhen using timeouts or reading from pipes that might close. - Use separate file descriptors when reading from files inside loops that also need interactive input.
Frequently Asked Questions
The basic read command (without options) is POSIX-compliant and works in sh, dash, bash, and zsh. However, options like -p, -s, -t, -a, -N, and -d are Bash-specific. Scripts using these features must run with #!/bin/bash, not #!/bin/sh. BusyBox environments (common in Alpine and embedded systems) have limited read support.
When you pipe into read (like echo "hello" | read var), the read command runs in a subshell. Variables set in subshells are discarded when the subshell exits. Use a herestring instead (read var <<< "hello") or process substitution to keep read in the current shell where the variable persists.
Set IFS to an empty value before read: IFS= read -r line. The empty IFS prevents word splitting that removes leading and trailing whitespace. The -r flag prevents backslash interpretation. Always quote the variable when using it ("$line") to preserve the whitespace during expansion.
Yes. Use read -n 1 to capture a single character immediately without requiring Enter. For menus or confirmations, this creates a smoother user experience. Combine with -s to hide the keypress and -t for a timeout if no key is pressed.
Final Thoughts
The read command handles user input, file processing, and data parsing in Bash scripts. The key patterns covered here include prompting with -p, hiding input with -s, setting timeouts with -t, and preserving raw data with IFS= read -r. With these techniques, you can build scripts that handle interactive prompts, parse CSV files, implement confirmation dialogs, and process log files reliably. For downloading files and making API calls in your scripts, explore the wget and curl commands.
Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed:
<code>command</code>command<strong>bold</strong><em>italic</em><a href="URL">link</a><blockquote>quote</blockquote>