As a system administrator or developer, directly interacting with the user via the command line is a common need. But bash doesn‘t include a built-in method for reading input. This is where the humble but powerful read command comes in handy.

Let‘s thoroughly explore how to leverage read to effortlessly collect user input in your bash scripts.

Why Read Over Other Input Methods

You may be wondering – why use read rather than other ways of getting input?

Bash does provide alternate options to retrieve data:

  • Command arguments$1, $2 etc access params given when invoking scripts
  • Command substitution – inline using $(cmd) or backticks

However, read has distinct advantages:

  • More flexible/interactive vs one-shot command args
  • Avoids ugly nested command substitutions
  • Stores input directly into clean variable names
  • Additional options to control input behavior
  • Portable across major shells like bash/zsh/ksh

For routine user input, read should be your first choice.

Read Command Syntax and Behavior

The basic syntax for read is straightforward:

read [options] [variable1] [variable2] ...

Without any arguments, read will retrieve input from standard input and store it in the special $REPLY variable.

For example:

read 
echo "Input was: $REPLY"
$ ./script.sh
Hello world
Input was: Hello world

To save the input into a specific variable, just pass the variable name:

read myvar
echo "The value stored was: $myvar" 

This makes collecting user input easy while keeping your code tidy.

By default word splitting is done on whitespace to allow handling multiple values.

Now let‘s look at some cases demonstrating the power of read.

Real-World Read Usage Examples

While the basics of read are simple, creative applications unlock additional capabilities:

Reading CSV Data into Variables

read neatly handles comma-separated values:

echo "Enter name,age,email"
read name age email 

echo "$name is $age years old at $email"

It can also populate arrays:

read -a userData

echo "Name: ${userData[0]}" 
echo "Email: ${userData[2]}"

Collecting Confirmation Prompts

Add an interactive confirmation with custom handling based on yes/no response:

read -p "Install package (y/n)? " confirm  

if [[ "$confirm" =~ ^([yY][eE][sS]|[yY])$ ]]; then
  echo "Installing ..."
else
  echo "Installation cancelled"
  exit  
fi

Secure Password Entry

read makes this effortless whereas most other approaches print sensitive info:

read -sp "DB Password: " dbpass; echo

export DB_PASS=$dbpass
# use $DB_PASS  

Building Interactive Menus

PS3="Choose an option: "

select opt in Install Remove Quit; do
  case $opt in
    Install)
      installMenu 
      ;;
    Remove) 
       removeMenu
       ;;
    Quit) break ;; 
  esac
done

Menus pair nicely with read for intuitive UIs.

Implementing Timeouts

Ever need the user to provide input within an allotted time window?

timeout=10
read -t $timeout -p "Enter something: "

if [[ $? -ne 0 ]]; then
   echo "Too slow!"
fi

Reading Multiple Inputs

read can handle multiple inputs at once by specifying multiple variables:

read firstName lastName age

echo "$firstName $lastName is $age years old"  
$ ./script.sh
John Doe 25 
John Doe is 25 years old

The input is split on any whitespace by default. This behavior can be overridden but whitespace field splitting suits most use cases.

Useful Options

While the basics of read are straightforward, additional options unlock further capabilities:

Read Secret Input with -s

The -s flag prevents input from being visible as typed, useful for passwords:

read -sp "Enter DB password: " dbpass; echo 
echo "Password set"

Similar behavior can be implemented manually but -s handles obscuring seamlessly.

Timeout Reading with -t

Ever need the user to respond within a time limit or you want to discard input after a window expires? The -t option sets a timeout in seconds:

read -t 5 -p "Enter your name: " name 2>&1 >/dev/null

[ $? -eq 0 ] && echo "Hello $name" || echo "Sorry, too slow!"

This even works without a variable to simply throw away unused input.

The examples show discarding stdin after a timeout occurs but timeout duration could be dynamically calculated as well.

Read Limited Input with -n

To have read return after an exact number of characters, use the -n option:

read -n1 -p "Press any key to continue..."
echo "Continuing..." 

This allows simple single character input validation.

For example, vetting y/n input without needing pressing enter:

read -n1 -p "Install (y/n)? " ans
echo   

if [[ $ans =~ [Yy] ]]; then
   installPkg
fi

Disable Interpretation with -r

By default read will process certain escape sequences like \n or backslashes. The -r option disables this behavior to read raw input verbatim:

read -r input
echo "You entered: $input" 

Read Array Input with -a

While read var1 var2 works for distinct variables, the -a option populates array data:

read -p "Enter codes: " -a codes 

for n in ${codes[@]}; do
  echo $n  
done

Advanced Usage

While daily read usage may be simple, some advanced applications open further possibilities.

Process Substitution

Feed read using process substitution to act on transient files:

read line < <(grep pattern file.txt)
echo "Matching line: $line"

This temporary input stream is discarded when the subprocess completes.

Reading One Character at a Time

What if you need to process a stream one character instead of a line at a time?

By combining timeout and -n1 we can implement this:

while timeout 1 read -n1 -t1 char; do  
  process $char
done < input.txt

This shows how creatively composing options enables new use cases.

Non-Standard Input Sources

While read primarily sources from standard input, any file descriptor can be used:

exec 3< testfile 

read line <&3
# read continues from fd 3 

Stdin itself can even be redirected into read:

exec 0< testfile
read line # from redirected stdin

This flexibility supports read consuming content from sockets, pipes etc.

Piping Data into Read

Speaking of pipes, they integrate seamlessly with read to process command output:

ls -l /etc | while read line; do
  echo "Got $line" 
done

Here the ls output is iteratively piped into read.

Validating and Processing Input

Bash provides many built-in tools to validate or transform input collected by read.

For example, to ensure only a number within a range was entered:

read -p "Enter port [1-65535]: " port

if [[ ! $port =~ ^[0-9]+$ ]]; then
  echo "Invalid format entered" >&2; exit 1
elif [[ $port -lt 1 || $port -gt 65535 ]]; then 
  echo "Out of range" >&2; exit 1
fi 

echo "Using port $port"

We can leverage other utilities like awk, grep, sed etc. to process input as needed.

Ensuring Robust Scripts

While read is very convenient, some best practices should be followed:

  • Always check return value from read in case of errors
  • Handle EOF scenario if read output is critical
  • Validate input data matches expected format/range
  • Use whitespace delimiting correctly for multiple variables
  • Avoid mixing options like -t and -n

Issues may arise from unreliable input streams or users manually terminating scripts.

Planning for adverse situations will ensure your scripts don‘t crash unexpectedly.

Read Keeps You Ahead

Whether used for simple user prompts or complex input scenarios, read is a versatile bash built-in. I hope this deep dive has revealed fresh techniques on how to use read effectively.

The examples only scratch the surface – with bash‘s rich facilities for variables, flow control, subshells and streams the possibilities are endless!

Challenge yourself to experiment with read and see where it can simplify and enhance your shell scripts.

Similar Posts