When you type ls *.txt, the shell replaces *.txt with the list of
matching files before running ls. That is an expansion: the shell
transforms what you write into something more precise. Understanding these
mechanisms lets you write short, powerful commands instead of repeating
the same operations by hand.
What you will learn
- Use the wildcards (
*,?,[...]) to target files - Generate sequences with the braces
{a,b}and{1..5} - Use the tilde
~as a shortcut to the home directory - Insert the result of a command with
$() - Compute a simple calculation with
$(()) - Choose between single and double quotes
What is globbing?
Globbing (pattern expansion) is the most used mechanism. The shell replaces the patterns with the list of files that match them.
The star *: zero or more characters
The star matches any run of characters, including none. It is the wildcard you will type most often:
ls *.txtShows every file whose name ends in .txt in the current
directory.
ls report*Shows every file whose name starts with report.
The question mark ?: exactly one character
The question mark stands for one character, no more and no less, which makes it stricter than the star:
ls file?.txtMatches file1.txt, fileA.txt, but not file12.txt (two
characters after file).
Brackets [...]: one character from a list
Brackets also match a single character, but only one of those listed:
ls file[123].txtMatches file1.txt, file2.txt, file3.txt.
You can also use a range:
ls file[a-z].txtMatches filea.txt, fileb.txt, and so on.
Combine the wildcards
Wildcards can appear anywhere in a path, and they mix with the braces seen further down:
ls /var/log/*.logls /etc/*.d/ls *.{txt,md}Test without risk
To see what the shell will expand, use echo instead of the real
command:
echo *.txtThe shell prints the list of matching files without running anything.
How do braces generate sequences?
Braces {} generate a list of values. Unlike globbing, braces
do not depend on existing files.
Explicit list
The simplest form lists the values between braces, separated by commas and without spaces. The shell then produces as many arguments as there are items, and this expansion happens before the command runs.
echo {red,green,blue}red green blueIt is handy to create several folders at once with mkdir -p, in a single command:
mkdir -p project/{src,docs,tests}This creates three subfolders in project/.
Numeric sequence
The two dots between two numbers produce the full range, bounds included. It is the form used to create numbered files or directories in a single command, without writing a loop.
echo {1..5}1 2 3 4 5touch file{01..10}.txtThis creates file01.txt to file10.txt.
Alphabetical sequence
The same mechanism works on letters, in alphabetical order. The benefit is the same: produce a series of names that do not exist yet, which globbing cannot do since it starts from the files that are present.
echo {a..f}a b c d e fHow does the tilde expand?
The ~ character is replaced by the path of your home directory, before the command runs:
echo ~/home/bob~/Documents is the same as /home/bob/Documents.
The tilde also accepts an attached account name: ~alice designates
Alice's home directory, that is /home/alice, provided the account
exists on the machine. Otherwise, the shell leaves the string as is,
which produces a literal ~alice path and a file-not-found error.
How does command substitution $() work?
The $(command) syntax runs the command between the parentheses and replaces the
whole thing with its output:
echo "Today is $(date +%Y-%m-%d)"Today is 2026-04-06Another example: store the result in a variable, to reuse it in the rest of a script:
FILE_COUNT=$(ls | wc -l)echo "There are $FILE_COUNT files here"Command substitution is very useful in scripts to capture information dynamically.
How does arithmetic $(()) work?
Bash can compute without calling an external program, on one condition: it
only handles integers. A division therefore returns an integer quotient,
$((7 / 2)) being 3 and not 3.5, and there are no decimal numbers.
For a decimal calculation, you need a tool such as bc or awk.
To compute with integers, put the expression between double parentheses preceded by a $:
echo $((3 + 7))10Available operators: +, -, *, / (integer division), % (modulo).
TOTAL=$((512 * 1024))echo "$TOTAL bytes"Single or double quotes: which one should you use?
The choice of quotes decides whether the shell interprets variables and expansions or not.
Double quotes "...": the shell interprets
Inside double quotes, the shell still replaces variables and substitutions:
NAME="Alice"echo "Hello $NAME"Hello AliceThe expansions $NAME, $(command) and $((calculation)) are interpreted.
Single quotes '...': the shell interprets nothing
Single quotes disable every expansion: variables, command
substitutions and calculations are passed as they are. They are therefore
the ones to use for literal text, a regular expression, or a password
containing a $.
echo 'Hello $NAME'Hello $NAMEThe text is taken literally.
When to use which
The rule fits in one question: do you want the shell to replace something in the text? If so, double quotes; if not, single quotes.
| Situation | Quotes |
|---|---|
| Text with variables to replace | "..." |
| Literal text with no replacement | '...' |
| Command with spaces in a path | "..." |
A habit to adopt right now: always wrap your variables in double
quotes, in the form "$MY_VAR". Without them, a space in the value
is read as an argument separator, and a command that was supposed to
receive one file name receives two. The defect only shows with values
that contain a space, so never during tests.
Troubleshooting
These 5 symptoms share a common root: expansion happens in the
shell, before the command runs. The error message therefore comes from the
command, which received something other than what you thought you gave it,
and never mentions the expansion itself. When in doubt, replace the command
with echo to see what it would have received.
| Symptom | Likely cause | Fix |
|---|---|---|
ls *.txt prints cannot access '*.txt': No such file or directory | No .txt file in the directory: Bash passes the pattern unchanged | Check with ls, then adjust the pattern |
mkdir {a, b} creates two folders named {a, and b} | Space after the comma: the braces are no longer expanded | Write {a,b} with no spaces and without '...' |
$NAME prints the literal text | Variable wrapped in single quotes '...' | Use double quotes "..." |
$(3 + 7) prints 3: command not found | Single parentheses: $() runs a command instead of computing | Write $((3 + 7)) with double parentheses |
$((1 / 2)) returns 0 | Bash only computes with integers: division truncates | Multiply before dividing, or use bc or awk for decimals |
Key points
- Globbing (
*,?,[...]) matches the names of existing files. - Braces
{a,b}and{1..5}generate lists, independently of files. ~is the shortcut to your home directory.$(command)inserts the result of one command into another.$(())handles integer calculations.- Double quotes
"..."interpret variables, single quotes'...'do not.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Customise your shell
A new full quiz with random questions
Work again only on the questions you missed