Table of Contents
- Basic Input with the
readCommand - Enhancing Interactivity: Custom Prompts and Sensitive Input
- Validating User Input: Ensuring Quality and Safety
- Advanced Techniques: Menus, Arguments, and Pipes
- Error Handling and User Feedback
- Real-World Example: A User Setup Script
- Best Practices for Interactive Scripts
- Conclusion
- References
1. Basic Input with the read Command
The foundation of interactive bash scripts is the read command. It reads a line of input from the user (or another source) and stores it in a variable. Let’s start with the basics.
Syntax of read
The simplest form of read is:
read variable_name
When executed, the script pauses, waits for the user to type input, and press Enter. The input is then stored in variable_name.
Example: Reading a Name
#!/bin/bash
echo "What is your name?"
read name # Wait for user input and store in $name
echo "Hello, $name! Welcome to the script."
Output:
What is your name?
Alice
Hello, Alice! Welcome to the script.
Key Options for read
The read command has several options to customize behavior. Here are the most useful:
| Option | Purpose | Example |
|---|---|---|
-p "prompt" | Display a prompt without a newline. | read -p "Enter age: " age |
-t timeout | Exit with status 124 if input isn’t received in timeout seconds. | read -t 10 -p "Enter input (10s): " input |
-n num | Read num characters (no need to press Enter). | read -n 1 -p "Continue? (y/n): " confirm |
-s | Silent mode: hide input (useful for passwords). | read -s -p "Enter password: " pass |
-r | Disable backslash escaping (treat input literally). | read -r "Enter path: " path |
Example: Using read -p for Concise Prompts
Instead of separate echo and read commands, use -p to combine them:
read -p "Enter your favorite color: " color
echo "Your favorite color is $color."
2. Enhancing Interactivity: Custom Prompts and Sensitive Input
To make scripts feel polished, you’ll need to go beyond basic prompts. Let’s explore how to customize prompts and handle sensitive data like passwords.
Customizing Prompts with Colors and Formatting
ANSI escape codes let you add color, bold, or underline to prompts, making them more readable. For example:
# Define color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
BOLD='\033[1m'
NC='\033[0m' # No Color
# Prompt with bold green text
read -p "${BOLD}${GREEN}Enter your username: ${NC}" username
Output: The prompt will appear in bold green, making it stand out.
Handling Sensitive Input (Passwords/PINs)
Use the -s flag to hide input when collecting passwords. Always pair this with a confirmation step to avoid typos:
read -s -p "Enter password: " pass
echo # Add a newline (since -s suppresses the user's Enter)
read -s -p "Confirm password: " pass_confirm
echo
if [ "$pass" = "$pass_confirm" ]; then
echo "Passwords match!"
else
echo "Passwords do NOT match. Exiting."
exit 1
fi
Limiting Input Length with -n
For PINs or yes/no prompts, use -n to read a specific number of characters (no need for Enter):
read -n 1 -p "Do you want to proceed? (y/n): " confirm
echo # Newline after input
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
echo "Proceeding..."
else
echo "Aborting."
fi
3. Validating User Input: Ensuring Quality and Safety
Invalid input can break scripts or cause unintended behavior. Always validate input early to catch issues before they escalate.
Common Validation Scenarios
1. Checking for Empty Input
Prevent users from submitting blank responses:
while true; do
read -p "Enter your email: " email
if [ -z "$email" ]; then # -z checks if variable is empty
echo "Error: Email cannot be empty. Try again."
else
break # Exit loop if input is valid
fi
done
echo "Email set to: $email"
2. Validating Data Types (Numbers, Emails, etc.)
Use regular expressions (via [[ $var =~ regex ]]) to enforce formats like numbers or emails:
Example: Validate a Number
while true; do
read -p "Enter your age (1-120): " age
if [[ "$age" =~ ^[0-9]+$ ]]; then # Regex: digits only
if [ "$age" -ge 1 ] && [ "$age" -le 120 ]; then # Check range
break
else
echo "Error: Age must be between 1 and 120."
fi
else
echo "Error: Please enter a valid number."
fi
done
echo "Age set to: $age"
Example: Validate an Email
email_regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
while true; do
read -p "Enter email: " email
if [[ "$email" =~ $email_regex ]]; then
break
else
echo "Error: Invalid email format. Try again."
fi
done
3. Checking for Existing Files/Users
For system scripts, validate that resources (e.g., users, files) exist or don’t exist:
while true; do
read -p "Enter username to create: " username
if id "$username" >/dev/null 2>&1; then # Check if user exists
echo "Error: User $username already exists."
else
break
fi
done
echo "Creating user: $username"
4. Advanced Techniques: Menus, Arguments, and Pipes
Take interactivity further with menus, command-line arguments, and input from external sources.
Menu-Driven Interfaces with select
The select loop creates simple menus. It displays options, reads user input, and executes actions:
echo "Choose an option:"
select option in "Create user" "Delete user" "Exit"; do
case $option in
"Create user")
echo "Starting user creation..."
# Add user creation logic here
break # Exit menu after selection
;;
"Delete user")
echo "Starting user deletion..."
break
;;
"Exit")
echo "Exiting..."
exit 0
;;
*) # Handle invalid input
echo "Invalid option $REPLY. Try again."
;;
esac
done
Output:
Choose an option:
1) Create user
2) Delete user
3) Exit
#? 1
Starting user creation...
Combining Command-Line Arguments with Interactive Prompts
Use getopts to handle command-line arguments, but fall back to interactive prompts if arguments are missing:
#!/bin/bash
# Default values
username=""
age=""
# Parse command-line arguments
while getopts "u:a:" opt; do
case $opt in
u) username="$OPTARG" ;;
a) age="$OPTARG" ;;
\?) echo "Invalid option -$OPTARG" >&2; exit 1 ;;
esac
done
# Prompt for missing arguments
if [ -z "$username" ]; then
read -p "Enter username: " username
fi
if [ -z "$age" ]; then
read -p "Enter age: " age
fi
echo "User: $username, Age: $age"
Usage:
# With arguments
./script.sh -u "Alice" -a 30
# Without arguments (prompts)
./script.sh
Reading Input from Files or Pipes
Scripts can accept input from files or pipes instead of the user. Use read in a loop to process lines:
# Read from a file
while IFS= read -r line; do
echo "Line: $line"
done < input.txt # Input file
# Read from a pipe
echo -e "apple\nbanana\ncherry" | while IFS= read -r fruit; do
echo "Fruit: $fruit"
done
5. Error Handling and User Feedback
Clear error messages and graceful retries make scripts user-friendly. Here’s how to handle issues effectively.
Providing Clear Error Messages
Avoid vague errors like “Invalid input.” Instead, explain what went wrong and how to fix it:
read -p "Enter a number between 1-10: " num
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
echo "Error: '$num' is not a number. Please enter digits only."
elif [ "$num" -lt 1 ] || [ "$num" -gt 10 ]; then
echo "Error: Number must be between 1 and 10. You entered $num."
fi
Retrying on Invalid Input
Use a while true loop to retry until valid input is received:
while true; do
read -p "Enter a positive number: " num
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -gt 0 ]; then
break # Valid input: exit loop
else
echo "Invalid input. Please enter a positive number."
fi
done
echo "You entered: $num"
6. Real-World Example: A User Setup Script
Let’s combine everything into a script that creates a user account with validation:
#!/bin/bash
# Define color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
# Step 1: Get and validate username
while true; do
read -p "${GREEN}Enter username: ${NC}" username
if [ -z "$username" ]; then
echo "${RED}Error: Username cannot be empty.${NC}"
elif id "$username" >/dev/null 2>&1; then
echo "${RED}Error: User '$username' already exists.${NC}"
else
break
fi
done
# Step 2: Get and validate age
while true; do
read -p "${GREEN}Enter age (1-120): ${NC}" age
if ! [[ "$age" =~ ^[0-9]+$ ]]; then
echo "${RED}Error: '$age' is not a number.${NC}"
elif [ "$age" -lt 1 ] || [ "$age" -gt 120 ]; then
echo "${RED}Error: Age must be between 1-120.${NC}"
else
break
fi
done
# Step 3: Get and confirm password
while true; do
read -s -p "${GREEN}Enter password: ${NC}" pass
echo
read -s -p "${GREEN}Confirm password: ${NC}" pass_confirm
echo
if [ "$pass" = "$pass_confirm" ]; then
break
else
echo "${RED}Passwords do not match. Try again.${NC}"
fi
done
# Step 4: Display summary
echo -e "\n${GREEN}=== User Summary ===${NC}"
echo "Username: $username"
echo "Age: $age"
echo "Password: [hidden]"
echo -e "${GREEN}Setup complete!${NC}"
7. Best Practices for Interactive Scripts
Follow these guidelines to build reliable, user-friendly scripts:
- Keep prompts clear and specific: Avoid ambiguity (e.g., “Enter your name” vs. “Input”).
- Validate input early and often: Catch errors before they cause failures.
- Handle edge cases: Empty input, timeouts, invalid types, and reserved values (e.g.,
root). - Limit sensitive data exposure: Never log passwords; use
-sfor input and avoid storing plaintext. - Test with diverse inputs: Test empty values, special characters, and out-of-range numbers.
- Document prompts and validations: Add comments explaining why certain checks are needed.
8. Conclusion
Interactive bash scripts bridge the gap between automation and user control. By mastering the read command, validation techniques, and error handling, you can create scripts that are not only powerful but also a joy to use.
Start small: build a simple menu-driven tool, then layer in validation and customization. With practice, you’ll be creating scripts that adapt to user needs while maintaining robustness.