Right, let’s talk about variables. This is where we stop just typing commands and start actually programming in bash. It’s also where bash, in its infinite, crusty wisdom, will happily wait for you to make a mistake and then blow up your script. I’m here to make sure that doesn’t happen.

First, the golden rule: there are no data types. Everything is a string. Want a number? It’s a string that happens to have digits. Want an array? We’ll get to that nightmare later. For now, just know that you’re working with text. This is both bash’s greatest simplicity and the source of its most hair-pulling frustrations.

The Absolute Basics: Assignment and Reference

You assign a value to a variable like this: varname=value. Notice what’s missing? Spaces around the =. If you put spaces in, bash thinks you’re running a command called varname with = and value as arguments. It will, quite rightly, tell you it can’t find that command. This is the first rite of passage for a bash scripter.

To get the value back, you use the $ operator: $varname. This is called variable expansion.

# Correct
name="Valentina"
echo "Hello, $name"  # Output: Hello, Valentina

# Incorrect (will error)
name = "Valentina"   # bash: name: command not found

Simple, right? Welcome to the trap. The $varname syntax is fine until it’s not. What if you want to print “Hello, $nameTerran”? bash will look for a variable called nameTerran, which doesn’t exist, and give you nothing. This is why the ${} syntax exists.

Why You Must Use ${var} (Seriously, Do It)

The ${varname} syntax is unambiguous. It explicitly tells bash where the variable name starts and ends. You should use it always, everywhere, especially in scripts. It makes your code readable and saves you from a whole class of bizarre bugs.

planet="Terran"
echo "Hello, $name$planet"    # Ambiguous and broken: Hello, 
echo "Hello, ${name}${planet}" # Explicit and correct: Hello, ValentinaTerran

# A classic example: appending text
file="data"
echo "The file is $file_backup"    # Tries to find variable 'file_backup'
echo "The file is ${file}_backup"  # Correct: The file is data_backup

See? The ${} syntax isn’t just for show; it’s your first line of defense against the interpreter doing something stupid because you were ambiguous.

The Quoting Dilemma: “”, ‘’, and `

This is the part that confuses everyone, so pay attention. It’s about controlling the shell’s expansion frenzy.

  • Double Quotes ": These are your best friends. They allow variable expansion ($var), command substitution ($(command)), and escape sequences (\n), but they prevent word splitting. This means whatever is inside the quotes is treated as a single word, even if it has spaces. You should almost always quote your variable expansions.

  • Single Quotes ': These are the isolation tanks. Nothing gets expanded inside single quotes. '$var' is literally the string $var. Use them when you need to guarantee that nothing changes.

  • Backticks `: These are the old, deprecated way to do command substitution. Use $(command) instead. It’s easier to read and, crucially, it nests properly. `` echo ls``` is a good way to confuse yourself, while $(echo $(ls)) is perfectly clear.

The biggest pitfall? Forgetting to quote variables. Let’s say you have a filename with a space: file="my report.txt". Now try to cat $file. bash sees cat my report.txt—three separate arguments. It tries to cat the file my and the file report.txt. Cue the error messages.

filename="my report.txt"
touch "$filename"    # Creates one file: "my report.txt"

# The disaster
ls $filename        # ls: cannot access 'my': No such file or directory
                    # ls: cannot access 'report.txt': No such file or directory

# The salvation
ls "$filename"      # Correct! Lists: my report.txt

Best Practice: Always quote your variable expansions unless you have a very specific reason not to. That reason is almost never “I forgot to.”

“Special” Variables and Default Values

Since your scripts will inevitably deal with user error (yours or someone else’s), you need ways to provide fallbacks. The ${var:-default} syntax is a lifesaver. If var is unset or empty, it uses default.

echo "Hello, ${name:-stranger}"  # If $name is not set, it uses "stranger"

The related ${var:=default} does the same thing but also assigns the default value to the variable. Useful for setting missing configuration.

And then there are the positional parameters. $1, $2, etc., for arguments passed to your script. $0 is the script’s name. $# gives you the number of arguments. Always check these.

#!/bin/bash
# A semi-robust script starter
target_file="${1:-/tmp/default.file}"  # Use first argument or a default
backup_dir="${2:-/backups}"

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <file> [backup_dir]" >&2
    exit 1
fi

echo "Backing up $target_file to $backup_dir"

This script doesn’t just fail mysteriously if you forget an argument; it gives you a sensible default and a helpful message. That’s the difference between a script that works and one that’s a pleasure to use.