Bash Read: Handling Input Data Easily

Bash's read tool makes getting input into variables a cinch. Whether you need user input, to read a file line-by-line, or even redirect output from another process into your script -- the read tool can offer help!
bash read tool alpharithms

Bash is a common scripting language that has evolved much over the years. It provides developers with a toolset to complete quick-and-dirty automation to help streamline productivity. Among its many features is the read tool which helps get input from the command line, prompting the user, or even via redirection and piping.

Highlights

Bash’s read tool can be used in a number of common ways as well as with a number of common options enabled (i.e. flags). By the end of this article the following applications of bash’s read tool will be covered:

  • Prompting and reading input from a user with echo and read.
  • Prompting and reading input from a user with only the read tool.
  • Disabling automatic newline characters.
  • Dealing with escape characters (slashes) in text.
  • Using read to save the input to multiple variables.
  • Using read to get input from a file, line-by-line.
  • A basic discussion of Internal Field Separators (IFS).

This article is by no means an exhaustive display of the read tool’s full gamut of functionality. However, most common cases are addressed here such that anyone should feel comfortable using the read tool in bash scripting afterward.

Read User Input

The most basic example of the read tool is to prompt a user to enter some input. This can be accomplished via the following approach:

#!/bin/bash

# prompt a user to enter a username.
echo "Enter your username: "

# use read command to store input to
# the <username> variable.
read username

# print the username to the console
# as a confirmation of success.
echo "Hello, $username"

This script executes the following three instructions:

  1. Prompts a user to enter a username.
  2. Stores the user’s input into a variable named username.
  3. Prints a dynamic string using the username variable to confirm.

The full sequence appears as such in the terminal window:

Enter your username: 
Zack
Hello, Zack

Quick and dirty, but gets the job done. Before we move on to the next example using the read command let us pick this one apart first. You might notice the input Zack typed is on the line following the prompt to the user.

Keeping User Input on the Same Line

To keep the user-entered input on the same line as the prompt, one can pass the -n flag to the echo tool which will, according to the man pages, instruct the echo tool: “do not output the trailing newline.” We just need to change the first line to the following:

echo -n "Enter your username: "

This results in the following output provided the name Zack is entered:

Enter your username: Zack
Hello, Zack

This keeps the output a bit tighter and arguably makes things easier to debug in many cases. Getting back on track, let’s consider an edge case quickly where slash characters might be entered.

Aside: Dealing with Escaped Characters

If you are developing bash scripts using an IntelliJ-based IDE you may have already noticed a warning that “read without -r will mangle backslashes.” This message urges users to use the -r flag that, according to the man page, instructs read to “… not allow backslashes to escape any characters.” Let’s consider an example of when this would cause an issue:

# defines a string variable
base="alpha\rithms";

# read in base variable without
# disabling escape feature
read var1 <<< "$base"
echo "$var1";

# read in base variable with
# escaping disabled
read -r var2 <<< "$base";
echo "$var2"

Here we define the variable base as a string with an escaping backslash in the midst of the characters alpha\rithms. We then use the <<< (here string) operator to explicitly pass this variable into the read command in two ways:

  1. Without the -r flag, allowing escaping characters to be present as typed.
  2. With the -r flag, removing escaping slashes from the text.

The output of this code is as follows:

alpharithms
alpha\rithms

This is particularly useful in cases where input may contain characters using the \ character for line continuations.

Prompt & Read User Input

Bash scripts are meant to be quick-and-dirty ways to automate tasks. At some point, using the echo command to facilitate gathering user input was deemed too tedious and the read tool was given a prompting feature. By passing the p flag, the read tool will both prompt and read user input. Consider the following:

# prompt user for name and
# store as variable
read -p "Enter a username: " username

# print the username to the console
# as a confirmation of success.
echo "Hello, $username"

The output of this code is as follows:

Enter a username: Zack
Hello, Zack

This approach demonstrates the -p option’s ability to remove the dependency of echo when gathering user input. Let’s now consider extending this use-case to prompt a user for their username and password. In the above example, we note that the username Zack was visible in the console — not a secure approach for gathering passwords. Using the -s flag, the read tool will “silence” the input such that it is not visible on the console. Consider the following:

# prompt user for name and
# store as variable
read -p "Enter a username: " username
read -sp "Enter a password: " password


# print the username to the console
# as a confirmation of success.
echo "Hello, $username. The length of your password is ${#password}."

Here we introduce a second variable password that prompts the user for input much like the prompting for username. The difference is the introduction of the -s flag to ensure the value of password is not displayed in the terminal as the user types. The final statement echos the length of the password (not secure) for demonstration here to validate that input was, in fact, received. The output of the above script is as follows:

Enter a username: Zack
Enter a password: Hello, Zack. The length of your password is 8.

Read Multiple Variables

Bash allows developers to read command line arguments with ease. While not diving too deeply into the use-cases, edge-cases, or finer points of command line arguments in bash — the following example shows how the read tool handles them.

#!/bin/bash

# prompt user for some integers
# and store to three variables a, b, c
read -p "enter some integers 0-9: " a b c

# print the three variables back to stdout
echo "Your integers are [$a] [$b] [$c]"

Here’s where things get a little … bashy. Let’s consider the output while entering 3 command line arguments: 1, 2, and 3:

enter 4 integers 0-9: 1 2 3
Your integers are [1] [2] [3]

Here we see each command line argument assigned to a variable, as expected, and printed out such that each of the three brackets in the echo string receives a single value. Now let’s consider the case where more than 3 command line arguments are given:

enter 4 integers 0-9: 1 2 3 4
Your integers are [1] [2] [3 4]

The output here is similar except that, for the last bracketed variable in the echo string — the [$3] — the last two command line arguments are assigned. This is expected behavior due to how bash handles command line arguments. Essentially, bash will assign the values of all command line arguments beyond the number of available variables for assignment, to the last variable. Consider one more case as an example:

enter some integers 0-9: 1 2 3 4 5 6 7 8 9
Your integers are [1] [2] [3 4 5 6 7 8 9]

Every value beyond the first two (1 and 2) are assigned to the third variable c. This is true for string values as well:

enter some integers 0-9: 1 2 3 4 5 6 cat dog fish horse
Your integers are [1] [2] [3 4 5 6 cat dog fish horse]

This behavior isn’t strictly related to the read command but useful given how the tool is commonly used. Check out this article by Redhat for a more detailed discussion on handling command line arguments in bash. Bash’s handling of command line args is somewhat strange when compared to other languages like Python, which shadows how other C-based languages handle them.

Read From a File Line-by-Line

The read tool can be used to read data linewise in from an external file. Assuming the file exists, is readable, and contains valid data the following approach will leverage the read command to read a file line-by-line:

#!/bin/bash

# iterate over word boundaries
while IFS= read -r line; do

  # print the line to the console
  echo "$line"

# redirect file contents
done < "../data/names.txt"

In this script, the following actions and options are taken/specified:

  1. A while loop takes input from a file.
  2. the IFS variable is set to the empty string, to preserve the whitespace.
  3. the < redirection operator is used to indicate the data source.
  4. the -r flag is used to avoid issues with slashes.

The output from this script displays the contents of the names.txt file which has a series of names listed as first, last separated by newlines:

bob, foo      
alice, delaney
garth, smith  
wayne, nebo   
bill, jones   
ted, jackson  
midge, marley

The output is exactly as expected! This demonstrates very basic, somewhat opinionated, usage of the while loop in which the read tool can be leveraged to read line-by-line from a file.

Final Thoughts

What is the best way to use the read tool? There is no easy answer here and like many things in the world of computer science and software engineering: it depends on what one is trying to do! The read command can be used along with the -p option to prompt and read user input which is a great time-saver as well as incredibly useful. Knowing the nuances of bash’s command line argument interpretations can ensure multiple variables can be accurately handled via the read tool as well.

Zαck West
Full-Stack Software Engineer with 10+ years of experience. Expertise in developing distributed systems, implementing object-oriented models with a focus on semantic clarity, driving development with TDD, enhancing interfaces through thoughtful visual design, and developing deep learning agents.