Read User Input in Bash Made Simple







Read User Input in Bash Made Simple | LinuxTeck

The bash read command to get user input with examples is something every Linux user eventually needs to understand. If you want your shell script to interact with users by asking for their names, passwords, or even simple yes/no confirmations, then the read command will be your best friend. Unlike some other commands, read is built into Bash, so there's no cost to learning it. Once you learn how to use it correctly, you can turn your scripts from single-use applications into truly interactive tools.

Why This Matters Before You Start:

Most articles online only show you the basic read name syntax and call it a day. But in real Linux work, you will run into problems that nobody warns you about:

  • Your script skips the read prompt entirely when run via cron or a pipe
  • You forget to reset IFS and it breaks the rest of your script
  • The -t timeout works differently inside a script vs on the terminal
  • Your read -s password field still echoes on some terminals

This guide covers all of that plus distro-specific differences, real DevOps examples, and a full cheat sheet nobody else has put together.

If you are just getting started with shell scripting, it helps to understand how bash itself works before diving into input handling. Check out this guide on what bash scripting is and how it works on Linux to get your footing first.

#01

What Is the read Command and How Does It Work

The read command is a bash built-in that pauses script execution and waits for the user to type something. Whatever the user types gets stored in a variable that you can use later in the script. It is the core tool for making any interactive bash script work properly.

Here is the simplest form storing a name the user types:

bash
LinuxTeck.com
#!/bin/bash
read name
echo "Hello, $name"
OUTPUT
John
Hello, John

The script pauses on the read name line. The user types "John" and hits Enter. The value gets stored in $name and echo prints it back. That is the full flow.

Important - Shebang Line Matters:

Always start your script with #!/bin/bash and not #!/bin/sh. On Ubuntu, /bin/sh points to dash, not bash. The read options like -e and -i are bash-only and will fail silently or throw errors under dash. On Rocky Linux and RHEL, /bin/sh usually points to bash, but it is still better to be explicit. The bash PATH and command lookup guide on LinuxTeck explains how Linux finds the right interpreter.

#02

Bash read Command to Get User Input - All Options With Examples

This is the section nobody else has done properly. Below is every useful flag for the read command options in bash, each with a working example and a plain explanation of what it actually does.

The -p Flag Show a Prompt

Instead of printing a message with echo and then calling read, use -p to do both in one line:

bash
LinuxTeck.com
#!/bin/bash
read -p "Enter your username: " username
echo "Welcome, $username"
OUTPUT
Enter your username: alice
Welcome, alice

The -s Flag - Silent Input for Passwords

The -s flag hides the characters the user types. Combine with -p to prompt and hide in the same line:

bash
LinuxTeck.com
#!/bin/bash
read -sp "Enter password: " password
echo -e "\nPassword stored."

Common Mistake:

Using read -s inside a subshell or over SSH with a pseudo-terminal can still show typed characters on some setups. This is a terminal emulator issue, not a bash bug. Always test password prompts over SSH if that is your target environment.

The -t Flag Timeout

Set a time limit in seconds. If the user does not respond, read returns a non-zero exit code and the script moves on:

bash
LinuxTeck.com
#!/bin/bash
if read -t 10 -p "Continue? [y/n]: " answer; then
echo "You chose: $answer"
else
echo "Timed out. Using default."
fi

Common Mistake:

The -t flag only works when bash is reading from a terminal. If your script gets its input from a pipe or a file, -t is ignored on some bash versions. Always use a default fallback with an else block when using timeouts.

The -n Flag Limit Input Characters

Stops reading after a set number of characters no need for the user to press Enter:

bash
LinuxTeck.com
#!/bin/bash
read -n 1 -p "Press y to confirm, n to cancel: " choice
echo
echo "You pressed: $choice"

The -a Flag Read Into an Array

Each space-separated word goes into a separate array element:

bash
LinuxTeck.com
#!/bin/bash
read -a servers -p "Enter three server names: "
echo "First: ${servers[0]}"
echo "Second: ${servers[1]}"
echo "Third: ${servers[2]}"
OUTPUT
Enter three server names: web01 web02 db01
First: web01
Second: web02
Third: db01

The -r Flag Raw Input

By default, bash treats backslash as an escape character in input. The -r flag disables that and treats every character literally. Use it almost always when reading file paths:

bash
LinuxTeck.com
#!/bin/bash
read -r -p "Enter full path: " filepath
echo "Path: $filepath"

Using IFS to Change the Delimiter

By default, read splits input on spaces. Change IFS (Internal Field Separator) to split on commas, colons, or any other character. Always restore IFS after use:

bash
LinuxTeck.com
#!/bin/bash
OLD_IFS="$IFS"
IFS="," read -r -p "Enter name,age,city: " name age city
IFS="$OLD_IFS"
echo "Name: $name | Age: $age | City: $city"
OUTPUT
Enter name,age,city: Alice,30,Berlin
Name: Alice | Age: 30 | City: Berlin

Common Mistake Forgetting to Reset IFS:

If you set IFS="," globally and forget to restore it, every loop, word split, and field separator operation in the rest of your script will break in unexpected ways. Always save the old value first and restore it right after, or scope it inline on the same line as read as shown above.

Full read Command Flags Quick Reference Table

Flag Syntax Example What It Does
-p read -p "Label: " var Display a prompt string before waiting for input
-s read -s -p "Pass: " pw Silent mode hides typed characters (for passwords)
-t N read -t 10 -p "..." var Timeout after N seconds; returns non-zero exit code
-n N read -n 1 -p "..." var Read exactly N characters, no Enter needed
-r read -r var Raw mode backslashes are literal, not escape chars
-a read -a arr Store space-separated words into an indexed array
-d CHAR read -d ":" var Stop reading at a custom delimiter instead of newline
-e read -e -p "..." var Enable readline editing (arrow keys, history) bash only
-i TEXT read -e -i "default" -p "..." var Pre-fill input with a default value (requires -e)
-u FD read -u 3 var Read from file descriptor FD instead of stdin
IFS=X IFS="," read -r a b c Change field separator for this read call only

#03

Input Validation With Loops and Regex

When writing real shell scripts, handling bad input is just as important as handling good input. If a user types something wrong, your script should ask again not crash or silently continue with invalid data. The cleanest way to do that in bash is to wrap your read call inside a while loop and only break out once the input passes your check.

Validate Non-Empty Input

bash
LinuxTeck.com
#!/bin/bash
while true; do
read -r -p "Enter your name (cannot be empty): " username
if [[ -n "$username" ]]; then
break
fi
echo "Name cannot be empty. Try again."
done
echo "Hello, $username"

Validate a Number Using Regex

bash
LinuxTeck.com
#!/bin/bash
while true; do
read -r -p "Enter a port number (1-65535): " port
if [[ "$port" =~ ^[0-9]+$ ]] && (( port >= 1 && port
OUTPUT
Enter a port number (1-65535): abc
Invalid port. Enter a number between 1 and 65535.
Enter a port number (1-65535): 99999
Invalid port. Enter a number between 1 and 65535.
Enter a port number (1-65535): 8080
Port set to: 8080

Set a Default Value if the User Just Presses Enter

bash
LinuxTeck.com
#!/bin/bash
read -r -p "Enter environment [default: staging]: " env
env=${env:-staging}
echo "Deploying to: $env"
OUTPUT
Enter environment [default: staging]:
Deploying to: staging

Tip Default Values Pattern:

The ${var:-default} syntax is a bash parameter expansion that returns the default when $var is empty or unset. It does not change the stored value. To also assign the default use ${var:=default}. The Linux shell scripting command cheat sheet on LinuxTeck has more of these patterns in one place.

#04

Interactive Bash Script With select Menu

Most tutorials never mention the select statement, which is a big oversight. When you want to give users a numbered list of choices, select is far cleaner than building your own menu with read and case manually. This is the backbone of a proper interactive bash script with select menu.

bash
LinuxTeck.com
#!/bin/bash
PS3="Choose your Linux distro: "
options=("Ubuntu" "Rocky Linux" "Debian" "Fedora" "Quit")
select opt in "${options[@]}"; do
case $opt in
"Ubuntu")
echo "You selected Ubuntu."
break ;;
"Rocky Linux")
echo "You selected Rocky Linux."
break ;;
"Debian")
echo "You selected Debian."
break ;;
"Fedora")
echo "You selected Fedora."
break ;;
"Quit")
echo "Exiting."
break ;;
*)
echo "Invalid option $REPLY. Try again." ;;
esac
done
OUTPUT
1) Ubuntu
2) Rocky Linux
3) Debian
4) Fedora
5) Quit
Choose your Linux distro: 2
You selected Rocky Linux.

Tip PS3 Variable:

The PS3 variable controls the prompt that select shows before each input. If you do not set it, bash defaults to #? which looks confusing to users. Always set a clear PS3 before your select loop. The variable $REPLY holds the raw number the user typed, while $opt holds the matched string from the list.

#05

Real-World DevOps Script Examples

These are the kinds of scripts you actually use on the job. No other article on this topic covers these use cases. Each one is a working template you can copy and adapt right away.

Example 1 Deployment Confirmation Script

Before pushing anything to production, you want a hard stop that forces the user to explicitly confirm. Here is the pattern most DevOps teams use:

bash
LinuxTeck.com
#!/bin/bash
ENV="production"
read -r -p "You are about to deploy to $ENV. Are you sure? [y/N]: " confirm
confirm=${confirm:-N}
if [[ "$confirm" =~ ^[Yy]$ ]]; then
echo "Deployment started..."
# your deploy commands go here
else
echo "Deployment cancelled."
exit 0
fi

Example 2 Backup Script With User-Selected Destination

A script that asks the user to pick a backup destination from a menu, then confirms before running:

bash
LinuxTeck.com
#!/bin/bash
SOURCE="/var/www/html"
PS3="Select backup destination: "
destinations=("/mnt/backup" "/tmp/backup" "/home/admin/backup" "Quit")
select dest in "${destinations[@]}"; do
case $dest in
"Quit") echo "Cancelled."; exit 0 ;;
"") echo "Invalid choice." ;;
*)
read -r -p "Back up $SOURCE to $dest? [y/N]: " ok
ok=${ok:-N}
if [[ "$ok" =~ ^[Yy]$ ]]; then
echo "Running backup..."
rsync -av "$SOURCE" "$dest"
else
echo "Backup cancelled."
fi
break ;;
esac
done

Tip Backup Scripts in Production:

For server-level backups you need more than a simple script. The Linux server backup solutions guide on LinuxTeck covers tools and strategies used in real enterprise environments.

Example 3 mapfile and readarray for Reading Files Into Arrays

The mapfile (also called readarray) reads lines from a file or command output directly into a bash array - no manual loops required:

bash
LinuxTeck.com
#!/bin/bash
# Read /etc/hosts lines into array, skip comments
mapfile -t hosts_lines < /etc/hosts
for line in "${hosts_lines[@]}"; do
[[ "$line" == "#"* ]] && continue
echo "Line: $line"
done

You can also capture command output into an array using process substitution:

bash
LinuxTeck.com
#!/bin/bash
mapfile -t running_services < <(systemctl list-units --type=service --state=running --no-legend | awk '{print $1}')
echo "Running services: ${#running_services[@]}"
for svc in "${running_services[@]:0:5}"; do
echo " $svc"
done

Common Mistake Pipe and Subshell Issue:

You cannot do command | mapfile -t arr directly because the pipe creates a subshell and the array will not be visible in the parent shell. Always use process substitution < <(command) as shown above to keep the array in the current shell context.

#06

Troubleshooting - Why read Is Not Working

Troubleshooting section on the read command. These are real problems you will face on the job, and here is how to fix each one.

Problem 1 read Skips Input When Script Is Called from a Pipe

If you run something like cat setup.sh | bash, the script's stdin is already occupied by the pipe. The read command immediately gets EOF and moves on without waiting. The fix is to read explicitly from the terminal:

bash
LinuxTeck.com
#!/bin/bash
# Read directly from the terminal even when stdin is piped
read -r -p "Enter your choice: " choice < /dev/tty
echo "Choice: $choice"

Problem 2 read Does Not Wait for Input in Cron Jobs

Cron runs scripts in a non-interactive shell with no terminal attached. Any read in a cron job will return immediately with an empty value. There is no fix for this read is not meant for unattended scripts. Move interactive prompts out of cron and pass values as script arguments instead:

bash
LinuxTeck.com
#!/bin/bash
# Cron-safe: accept input as arguments, not read prompts
ENV=${1:-staging}
echo "Running backup for environment: $ENV"

For more on scheduling and cron setup see the cron command guide with examples on LinuxTeck.

Problem 3 IFS Not Reset After Use

If you set IFS globally and forget to restore it, word splitting breaks everywhere downstream. Always save and restore:

bash
LinuxTeck.com
#!/bin/bash
# Safe IFS usage - save, use, restore
OLD_IFS="$IFS"
IFS=":" read -r user pass uid gid info home shell <<< "$(grep "^root" /etc/passwd)"
IFS="$OLD_IFS"
echo "Shell: $shell"

Problem 4 Distro-Specific Behavior Differences

Distro Default /bin/sh read -e / -i support Notes
Ubuntu 22.04 / 24.04 dash No (bash-only flags) Always use #!/bin/bash; dash ignores -e and -i
Rocky Linux 8 / 9 bash Yes Safe to use all flags; bash 5.x is default
RHEL 8 / 9 bash Yes Same as Rocky; enterprise bash 5.x
Alpine Linux busybox ash No Most bash-only flags fail; use POSIX-only read syntax
Debian 12 dash No Same as Ubuntu; always specify #!/bin/bash

Tip Comparing RHEL and Ubuntu for Server Use:

If you are deciding which distro to standardize on for your scripts, the RHEL vs Ubuntu Server comparison on LinuxTeck covers key differences including shell environments and scripting compatibility.

Problem 5 read With Pipes and Here-Strings

A common beginner trap is piping directly into read. The variable gets set inside a subshell and disappears when the pipe ends. Use a here-string instead:

bash
LinuxTeck.com
#!/bin/bash
# WRONG: variable lost after pipe
echo "hello world" | read word1 word2
echo $word1 # empty!

# CORRECT: use here-string
read word1 word2 <<< "hello world"
echo $word1 # hello

FAQ

Frequently Asked Questions

How do I read multiple inputs in a single line bash?

List multiple variable names after read and bash will assign each space-separated word to each variable in order. The last variable gets everything that is left over.

bash
LinuxTeck.com
read -r -p "Enter first last email: " first last email
echo "$first $last -- $email"

If you need a custom separator like a comma, set IFS before the read call and restore it right after. See Section 02 for the safe IFS pattern.

Why is bash read command not waiting for input?

The three most common reasons are:

  • Script is being piped stdin is already occupied. Fix: redirect from /dev/tty as shown in Section 06.
  • Running in cron or CI/CD no terminal is attached. read instantly returns empty. Remove interactive prompts from any unattended execution paths.
  • Using #!/bin/sh on Ubuntu some read flags do not exist in dash and the command behaves differently. Switch to #!/bin/bash.

For more on shell scripting error patterns see the bash exit codes and error handling guide on LinuxTeck.

How do I set a default value if the user just presses Enter?

Use bash parameter expansion right after the read call:

bash
LinuxTeck.com
read -r -p "Enter branch [default: main]: " branch
branch=${branch:-main}
echo "Using branch: $branch"

Alternatively use read -e -i "main" to pre-fill the input field bash only, not supported in dash.

Why does read -s still show input on some terminals?

The -s flag disables terminal echo via an ioctl call to the tty. If you are accessing the script over SSH without a pseudo-terminal (ssh -T), or if the terminal emulator overrides echo settings, the suppression may not work. Test with a real SSH session using a proper PTY. For production password handling consider using sudo with sudoers config or environment secrets rather than relying on read -s alone.

Can I use read in a cron job?

No, and you should not try. Cron jobs run without a terminal so read returns immediately with an empty value. Scripts that run unattended should accept configuration via arguments ($1, $2) or environment variables instead of interactive prompts. The cron command guide on LinuxTeck walks through the full scheduling setup.

How do I read input from a file instead of a user?

Redirect a file into a while loop using read to process it line by line:

bash
LinuxTeck.com
#!/bin/bash
# Process each line in servers.txt
while IFS= read -r line; do
echo "Connecting to: $line"
done < servers.txt

The IFS= before read prevents leading and trailing whitespace from being trimmed on each line. This is the canonical safe pattern for reading files in bash.

END

Summary

The bash read command to get user input with examples is more capable than most people realize. Beyond the basic -p and -s flags, you have a full toolkit for building interactive, validated, and menu-driven scripts that work reliably across Ubuntu, Rocky Linux, and RHEL.

The main things to remember: always use #!/bin/bash as your shebang, pair read with validation loops in production scripts, always restore IFS after changing it, and keep interactive prompts out of any script running in cron or CI/CD.

For a deeper look at scripting automation patterns used in real Linux environments, the Linux bash scripting automation guide on LinuxTeck is a solid next step.

🔗 Related Articles

LinuxTeck - A Complete Linux Learning Blog
Learn step-by-step how to automate Linux tasks with real-world scripts and practical examples.



About Sharon J

Sharon J is a Linux System Administrator with strong expertise in server and system management. She turns real-world experience into practical Linux guides on Linux Teck.

View all posts by Sharon J →

Leave a Reply

Your email address will not be published.

L