Copilot in the Terminal: Catch Shell Script Errors Before They Run
You've been there: a shell script runs, deletes the wrong directory, and the damage is done before you read the output. Shell is fast, terse, and unforgiving β a missing quote or a wrong variable expands into something catastrophic in milliseconds.
GitHub Copilot now works directly in the terminal, and one of its most practical uses isn't writing scripts from scratch β it's reviewing the one you just typed before you execute it. This article walks through exactly how to set that up and use it effectively.
What you'll learn
- How to install and configure Copilot in the terminal (GitHub CLI extension)
- How to ask Copilot to explain and review shell commands before running them
- Common shell script mistakes Copilot reliably catches
- How to build a pre-run review habit into your daily workflow
- Limitations to keep in mind so you don't trust it blindly
Prerequisites
You'll need a GitHub account with a Copilot subscription (Individual, Business, or Enterprise tier all work). The setup also requires the GitHub CLI (gh) installed and authenticated. If you're on macOS, brew install gh gets you there. On Linux, check your distro's package manager or the official install script at cli.github.com.
The examples here use Bash, but the same approach applies to Zsh, Fish, and PowerShell β Copilot understands all of them.
Installing Copilot in the Terminal
Once gh is installed and you've run gh auth login, adding Copilot to your terminal is a single command:
gh extension install github/gh-copilotVerify it installed correctly:
gh copilot --versionYou should see a version string. If you get a "command not found" error, make sure your PATH includes the gh extensions directory β typically ~/.local/share/gh/extensions on Linux or the equivalent on macOS.
The extension exposes two main subcommands: gh copilot suggest and gh copilot explain. Both are where you'll spend most of your time.
The Two Commands You'll Actually Use
gh copilot explain
Pass any shell command or short script and Copilot gives you a plain-English breakdown of what it does, step by step. This is most useful when you're about to run something you copied from a Stack Overflow answer or an internal runbook and you want to understand it first.
gh copilot explain "find . -type f -name '*.log' -mtime +30 -exec rm -f {} \;"The output will walk you through each flag: what -type f does, what -mtime +30 means in terms of file age, and why -exec rm -f {} \; is different from piping to xargs. It'll also flag that this command permanently deletes files with no trash recovery.
gh copilot suggest
Describe what you want to do in plain English, and Copilot returns a shell command. More useful than it sounds, because you can then pass that suggestion back through explain before running it:
gh copilot suggest "compress all png files in the current directory without losing quality"Copilot will ask which shell you're targeting, then return a command. You can choose to run it directly, copy it, or ask for a revision β all from the interactive prompt.
Reviewing a Script Before Running It
The real workflow improvement comes when you use explain as a pre-flight check on scripts you've written yourself. Paste the script body as a here-doc or a quoted string:
gh copilot explain <<'EOF'
#!/bin/bash
BACKUP_DIR="/backups"
rm -rf $BACKUP_DIR/*
rsync -av /var/data/ $BACKUP_DIR/
EOFCopilot's response will note that rm -rf $BACKUP_DIR/* expands the glob in the current shell before rm runs, and if BACKUP_DIR is ever empty or unset, the command becomes rm -rf /*. This is one of the most dangerous unquoted-variable bugs in Bash, and it's the kind of thing that's easy to miss in a quick read.
A safer version uses quotes and a guard:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backups"
if [[ -z "$BACKUP_DIR" ]]; then
echo "BACKUP_DIR is not set. Aborting."
exit 1
fi
rm -rf "${BACKUP_DIR:?}"/*
rsync -av /var/data/ "$BACKUP_DIR"/The ${BACKUP_DIR:?} syntax causes the script to abort with an error if the variable is empty or unset, before rm gets a chance to do anything. Copilot will often suggest this pattern when it flags the unquoted version.
Common Errors Copilot Reliably Catches
After using this workflow daily, a few categories of bugs come up consistently.
Unquoted variables in paths
Any variable used in a file path should be double-quoted. rm -rf $DIR is a foot-gun; rm -rf "$DIR" is not. Copilot flags this almost every time.
Missing set -euo pipefail
Without set -e, a script continues executing even after a command fails. Without set -u, undefined variables silently expand to empty strings. Without set -o pipefail, a failed command in a pipe is invisible. Copilot will recommend adding this line near the top of any Bash script that lacks it.
Glob expansion order issues
When a glob like *.bak matches nothing, most shells pass the literal string *.bak to the command instead of an empty list. Whether this matters depends on the command, but in destructive operations it can cause unexpected behavior. Copilot tends to mention this when it sees globs in rm, mv, or cp commands.
Redirect vs. append confusion
> truncates the file; >> appends. In logging scripts this mistake is common and silent β you lose previous log entries and never notice until you're debugging an incident. Copilot catches this in context when the surrounding script suggests accumulation was intended.
Incorrect use of sudo in scripts
Running sudo inside a script that will itself be run as root is redundant and sometimes dangerous. Conversely, scripts that should run as root but don't check for it can fail halfway through in ways that leave things in a broken state. Copilot often spots both patterns.
Building a Pre-Run Review Habit
The best time to review a script is right before you run it, not after something breaks. A practical approach: whenever you write a script longer than ten lines, run it through gh copilot explain before the first execution.
You can also create a shell function to make this frictionless. Add something like this to your ~/.bashrc or ~/.zshrc:
review_script() {
local file="$1"
if [[ -z "$file" ]]; then
echo "Usage: review_script <script_file>"
return 1
fi
gh copilot explain "$(cat "$file")"
}Now review_script deploy.sh sends your entire script to Copilot for explanation before you run it. It takes about ten seconds and has caught real issues more than once.
For scripts that run in CI or on a schedule, treat the Copilot review the same way you'd treat a code review: required before merge, not optional.
What Copilot Won't Catch
Copilot is a language model, not a static analysis tool. There are a few important gaps to keep in mind.
It doesn't know your environment. It can't tell you whether /var/data/ actually exists on your machine, whether a user has the right permissions, or whether a service needs to be stopped before a file is moved. Context that lives outside the script text is invisible to it.
It can be wrong about edge cases. For obscure shell behaviors β like how different versions of Bash handle arrays, or how a particular flag behaves on BSD vs. GNU coreutils β Copilot sometimes gives confident but incorrect answers. When correctness is critical, cross-check against man pages.
It doesn't replace shellcheck. shellcheck is a dedicated static analysis tool for shell scripts and it catches a different class of bugs with higher precision. Use both: Copilot for understanding and high-level review, shellcheck for systematic linting. Install it with apt install shellcheck or brew install shellcheck and run it as part of your CI pipeline.
Long scripts lose coherence. Scripts over 200β300 lines may get a generic explanation that glosses over specific risky sections. Break large scripts into smaller functions and review each one separately if you need thorough coverage.
Wrapping Up
Shell script errors are disproportionately expensive for how easy they are to make. A five-second review before running a script is almost always worth it, and having Copilot explain what a command actually does removes the temptation to just trust it and run.
Here are concrete next steps to put this into practice:
- Install
ghand the Copilot extension today if you haven't:gh extension install github/gh-copilot - Pick one script you run regularly and run
gh copilot explainon it β you may be surprised what it surfaces - Add
set -euo pipefailto every Bash script that doesn't already have it - Install
shellcheckand wire it into your editor or CI pipeline alongside Copilot - Create the
review_scriptshell function and make it part of your pre-execution checklist for any script longer than ten lines
π€ Share this article
Sign in to saveRelated Articles
Comments (0)
No comments yet. Be the first!