Master

Debugging a shell script

Shell scripts fail in ways that look unrelated to the code: a command runs in the wrong folder, a variable is empty, a glob matched nothing. The shell can tell you which of those it did, if you ask it to, and that trace is usually the whole answer.

What you will see

The expanded for line printed before it runs, showing the pattern passed through unchanged. That line is the diagnosis.

About 1 min of reading

Try It

Trace a failing script.

1

Write a script with a bug

printf '#!/bin/sh\nfor f in *.txt; do wc -l $f; done\n' > bad.sh && chmod +x bad.sh

Before you run it, say out loud what it will print. Then open the answer.

What it prints
A script that will fail if no .txt files exist.
2

Run in an empty folder

mkdir -p empty && cd empty && ../bad.sh

Before you run it, say out loud what it will print. Then open the answer.

What it prints
An error about a file named *.txt - the literal pattern reached wc, which tells you nothing matched.
3

Trace it

sh -x ../bad.sh

Before you run it, say out loud what it will print. Then open the answer.

What it prints
The expanded for line printed before it runs, showing the pattern passed through unchanged. That line is the diagnosis.

Data Flow

The script reports what it ran.
1 Run in trace mode
↓
2 Read the expanded commands
↓
3 Find the line that is not what you meant
↓
4 Fix the data or the quoting
Why this works
Quote and then trust

Most mysterious shell bugs are word splitting or globbing on a value you did not expect. Quoting every expansion removes that family of bugs, and the remaining failures are real logic you can read.

Small inputs first

Run the script against three files rather than three thousand. A bug that shows on three is the same bug, and the run takes a second instead of a minute and a half of unreadable output.

Examples

Trace every command

Running the script in trace mode prints each command after expansion, with its status. You see the actual arguments, so an empty variable or a bad glob is visible rather than guessed at.

Stop at the first failure

A script that stops on the first failing command tells you where it broke. Without that, the failure is somewhere earlier than the message and you search the whole file.

Check the expansion, not the code

Echo the expanded line before running it. When the line you see is not the line you meant, the bug is in the data reaching it, not in the logic around it.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← A script that runs anywhere
Previous Lesson
A script that runs anywhere
Next Lesson
Writing shell you can hand over
Writing shell you can hand over →