CTCaliberTek
Course: Shell Scripting for Linux and Unix: From First Script to Automation · Lesson 2 of 8Log in to track your progress and earn a certificate when you finish the course.
Programming

Bash Variables, Quoting, and Reading User Input

Published November 10, 2026·Updated November 10, 2026·10 min read

Edited by , Senior Data Analyst. How we make our content

Like SaveLog in to like, save, and track progress.

Introduction

Variables let a script remember values such as file names, dates, and user input and reuse them. Bash variables are simple, but they follow rules that surprise people coming from other languages, especially around spaces and quotes. Most real-world shell bugs come from unquoted variables, so this lesson spends time on quoting. You will also learn to capture the output of commands, do integer arithmetic, read input from the person running the script, and accept arguments from the command line.

Prerequisites

  • Completed the first lesson or can create and run a Bash script
  • A terminal with Bash 4 or newer is best, though almost everything here works in older versions

Step-by-Step Guide

Step 1: Create and use variables

Assign a variable with the name, an equals sign, and the value, with no spaces around the equals sign. If you add spaces, Bash treats the name as a command and fails. To read a variable, put a dollar sign in front of its name. Wrap the name in braces when other text follows it, so Bash knows where the name ends. By convention, uppercase names are for environment variables and constants, and lowercase names are for your own script variables.

name="Dev"
count=3
echo "Hello, $name. You have ${count} new messages."
echo "File: ${name}_report.txt"      # braces mark where the name ends

# Common mistakes:
# name = "Dev"        -> error: Bash thinks "name" is a command
# echo "$namereport"  -> empty: there is no variable called namereport

readonly SITE="calibertek.com"       # cannot be changed later
unset count                          # remove a variable

Step 2: Quote your variables

Double quotes keep a value together as one word. Without quotes, Bash splits the value on spaces and tabs and also expands wildcard characters such as the asterisk. A file named my report.txt would then be passed to a command as two separate arguments. The habit that prevents the most bugs is to put double quotes around every variable expansion unless you specifically want splitting. Single quotes go further and stop all expansion, so they are best for text that must stay literal.

file="my report.txt"
touch "$file"          # creates one file
ls -l $file            # wrong: Bash sees two arguments, "my" and "report.txt"
ls -l "$file"          # right

echo '$HOME stays literal in single quotes'
echo "$HOME is expanded in double quotes"

Step 3: Capture command output with command substitution

The syntax dollar sign followed by parentheses runs a command and replaces itself with the output, with trailing newlines removed. It is the way to store the result of a command in a variable. Prefer it over the older backtick form because it nests cleanly and is easier to read. Command substitution is also how you build file names that contain dates, which you will use in the backup project later in this course.

today=$(date +%F)
files=$(ls | wc -l)
echo "On $today this folder had $files entries"

backup_dir="/tmp/backup-$(date +%Y%m%d)"
echo "$backup_dir"

Step 4: Do arithmetic

Bash does integer arithmetic inside double parentheses, and the form with a leading dollar sign returns the result as text. Division discards the fraction, so 7 divided by 3 is 2. Variable names inside the parentheses do not need a dollar sign. For decimal numbers, hand the work to another tool such as awk or bc, because Bash itself has no floating point support.

a=7; b=3
echo $(( a + b ))        # 10
echo $(( a * b ))        # 21
echo $(( a / b ))        # 2   integer division
echo $(( a % b ))        # 1   remainder

count=0
count=$(( count + 1 ))
total=$(( count * 5 ))
echo "$total"            # 5

# decimals need another tool:
awk 'BEGIN { printf "%.2f\n", 7 / 3 }'     # 2.33

Step 5: Read input from the user

The read command stores a line of typed input in a variable. Always use the -r option so that backslashes in the input are kept as typed, and use -p to show a prompt. The -s option hides what is typed, which suits passwords. Input can be empty, so give it a default with the expansion described later in this lesson. Scripts that run unattended, for example from cron, should take values from arguments or the environment instead of waiting for someone to type.

read -r -p "Your name: " name
read -r -p "Continue? [y/N] " answer
answer=${answer:-N}
echo "Hi $name, you answered $answer"

read -r -s -p "Token: " token    # -s hides the typing
echo

Step 6: Use script arguments

Arguments typed after the script name arrive in numbered variables: $1 is the first, $2 the second, and so on, while $0 is the script name. The variable $# holds how many arguments there were. The form "$@" in double quotes expands to every argument as a separate word, preserving spaces inside each one, and it is almost always what you want when looping over arguments. The unquoted or star forms merge or split arguments in surprising ways. The shift command drops the first argument so that the next one becomes $1.

#!/usr/bin/env bash
# greet.sh
echo "Script name: $0"
echo "First argument: $1"
echo "Number of arguments: $#"
echo "All arguments:"
for arg in "$@"; do
  echo "  - $arg"
done

# run it:
# ./greet.sh Ada "Grace Hopper"

Step 7: Set default values and required values

Parameter expansion can supply a fallback when a variable is empty or unset. The colon-dash form uses a default without changing the variable. The colon-question form stops the script with an error message if the variable is missing, which is a clean way to demand required settings such as an API key. These forms turn many short if statements into a single readable line.

dir=${1:-/tmp}                          # first argument, or /tmp if missing
name=${NAME:-anonymous}
: "${API_KEY:?API_KEY must be set}"     # stop with an error if empty or unset
echo "Using $dir as $name"

Step 8: Trim and change strings

Bash can cut strings without calling other programs. The pound forms remove a pattern from the start of a value, and the percent forms remove one from the end, with the doubled symbol meaning the longest match. A slash replaces text, and the length form counts characters. Case conversion with the caret and comma forms needs Bash 4 or newer, so it is missing from the Bash 3.2 that ships with macOS.

path="/var/log/nginx/access.log"
echo "${#path}"                 # 25 characters
echo "${path##*/}"              # access.log      (remove longest prefix ending in /)
echo "${path%/*}"               # /var/log/nginx  (remove shortest suffix starting at /)

file="report.txt"
echo "${file%.txt}.csv"         # report.csv
echo "${file/report/summary}"   # summary.txt

word="shell"
echo "${word^^}"                # SHELL (Bash 4 and newer)

Step 9: Understand environment variables

Ordinary variables exist only inside your script. The export command marks a variable so that programs started by the script also receive it, which is how tools such as git or aws read their settings. Placing an assignment directly before a command sets the variable for that one command only. Use the env command to list what is currently exported.

export APP_ENV=production     # visible to programs this shell starts
APP_ENV=staging ./deploy.sh   # set only for this one command
env | grep APP_ENV

FAQs

Why can I not put spaces around the equals sign?
Bash decides what a line means from its first word. With spaces, the first word is the variable name, so Bash looks for a command with that name and reports command not found. Without spaces, the whole word is recognized as an assignment.
When should I use single quotes and when double quotes?
Use double quotes when you need a variable or command to be expanded, and single quotes when text must stay exactly as typed, such as a regular expression or an awk program containing dollar signs. If a string needs both, close one quote style and open the other, or use a here document.
What is the difference between "$@" and "$*"?
In double quotes, "$@" expands to each argument as its own word, so arguments containing spaces stay intact. The star form joins all arguments into a single word separated by spaces. Use "$@" when passing arguments on to another command or looping over them.

Summary

You now know how to create variables without spaces around the equals sign, why quoting every expansion prevents bugs, how to capture command output, do integer arithmetic, read input, handle arguments, supply defaults, trim strings, and export environment variables. In the next lesson you will use these values to make decisions with if statements, tests, and case.

Related tutorials

Comments

Log in or create a free account to join the discussion.

  • No comments yet. Be the first to comment.