TerminalVibes / Copy, Move, Delete
Part 3

Copy, Move, Delete — and the Habits That Keep You Safe

"The terminal assumes you mean exactly what you say. Deleting is the moment to be sure you do."

In Moving Around you learned to look and to create. Now come the tools that change things: copy, move, rename, delete. They're wonderfully fast — one line does what fifty drag-and-drops would — and they come with a responsibility the graphical world hid from you: there is no confirmation dialog, and for rm, no trash can. This part teaches the commands and, just as deliberately, the habits that make them safe.

Important
The safety net in the terminal isn't a feature — it's a ritual: look before you act (ls first), prefer interactive flags (-i) while learning, and preview every wildcard with echo before letting it loose. Every habit in this part exists because someone, somewhere, lost a day's work not having it.

3.1 Copying

Copying is the gentlest of the three power tools — the original is never touched. It's also your cheapest insurance policy: one cp before an experiment means there's always a way back.

cp duplicates: the original stays put, the copy goes where you point
Note
The stakes: You're about to let an AI agent rewrite config.yaml. If its "improvement" breaks everything, you want the original back in one command.

cp always reads the same way: source first, destination second — "copy this, to there." What "there" means depends on what the destination is:

The two shapes of cp
# Destination is a new name: copy + rename in one step
cp config.yaml config.yaml.bak

# Destination is an existing folder: copy INTO it, same name
cp config.yaml backups/
Tip
That first line is the backup-before-experiment pattern, and it's worth making a reflex: before any risky edit — yours or an agent's — cp file file.bak. Restoring later is just the reverse: cp config.yaml.bak config.yaml. (a Git-free time machine, until you have Git.)

Copying Folders Needs -r

Ask cp to copy a directory and it declines — a folder can contain thousands of files, and cp wants you to say you mean it:

cp -r for directories
cp projects backup-projects
# cp: -r not specified; omitting directory 'projects'

cp -r projects backup-projects   # -r = recursive: the folder and everything inside
Warning
cp overwrites silently. If the destination file already exists, cp replaces it — no question asked, old contents gone. While you're learning, add -i ("interactive"): cp -i config.yaml backups/ stops and asks overwrite? before clobbering anything — type y or n and press Enter, and a bare Enter counts as no. That -i belongs to cp, though, not to the terminal: sed takes the same letter and means something far sharper by it (Editing in Place). This same silent-overwrite rule returns with mv in the next section — it's a theme.
Vibe it

"Back up my config file before you change anything, and tell me the restore command"

"Copy the whole src folder to src-backup so we can experiment safely"

3.2 Moving & Renaming

Here's a small mind-bender: the terminal has no "rename" command, because it doesn't need one. Renaming is moving — moving a file to a new name in the same place. One command, mv, does both.

mv relocates or renames — same command, and the original doesn't stay behind
Note
The Problem: The agent scaffolded your project but named the main file untitled.py, and dropped three data files in the project's top folder that belong in data/. Tidy it up.

Like cp, it's always source first, destination second — and the same two shapes:

mv — rename or relocate
# Destination is a new name: RENAME
mv untitled.py main.py

# Destination is an existing folder: MOVE into it
mv sales.csv users.csv logs.csv data/

# Both at once: move AND rename
mv draft.md docs/chapter-1.md

The middle line is the fine print behind "source first, destination second": once you name more than two things, the last one has to be a folder, and it has to exist already. Neither cp nor mv will make one for you — with no docs/ yet, the last line stops at No such file or directory, and you fix it with mkdir docs and run it again.

Two nice surprises compared to cp: mv moves whole folders without needing -r (on the same disk it just relabels the folder's address — instant, even for gigabytes), and since nothing is duplicated, there's no copy lying around to get stale. Move to a different disk, though — an external drive, a USB stick — and mv quietly becomes a full copy followed by a delete: slow, and interruptible halfway through.

Warning
The overwrite danger, part two. If the destination name already exists, mv replaces it silently — and unlike cp, you lose both versions of the story: the destination's old contents are gone, and the source no longer exists under its old name. mv notes.txt ideas.txt when ideas.txt already exists destroys ideas.txt. The fix is the same reflex: mv -i asks before overwriting, and ls the destination first when in doubt.
Vibe it

"Rename untitled.py to main.py and move every CSV in this folder into data/"

"Reorganize these files into sensible folders — show me the mv commands before running them"

3.3 Deleting (Carefully)

This is the most important safety lesson in the entire course. Everything else in the terminal is forgiving — errors are harmless, cd can't hurt you, cp leaves originals alone. rm is the exception. Read this section twice.

rm doesn't move files to a trash can — it erases them, immediately and permanently
Caution
rm has no trash can. When your file manager "deletes" a file, it moves it to the Trash, where it sits recoverable for weeks. rm does no such thing: the file is gone the instant you press Enter. No confirmation, no undo, no recovery. Treat every rm as permanent, because it is.
The rm family
rm old-notes.txt        # Delete one file. Permanently.
rm draft1.md draft2.md  # Delete several
rmdir empty-folder      # Delete a folder — only works if it's EMPTY
rm -r old-project       # Delete a folder and everything inside it
rm -f missing.txt       # -f = force: no complaints if it doesn't exist

Each flag is reasonable alone. -r recurses into folders; -f suppresses prompts and "no such file" errors so scripts run clean. Combined as rm -rf, they mean: delete everything at this path, recursively, without asking, without stopping. Now add the two ingredients from your recent lessons and you have the terminal's most famous disaster recipe:

rm -rf — no questions

Recursive and forced: nothing slows it down, nothing asks "are you sure?"

+ a wildcard

* expands to "everything here" — however much that turns out to be (next section).

+ the wrong directory

You thought you were in ~/tmp. You were in ~/projects. The pwd habit exists for this moment.

A single mistyped space does it too: rm -rf old-app / (note the stray space) is not "delete old-app/" — it's "delete old-app, and also the entire filesystem starting at root." This exact typo has destroyed production servers. It's why modern systems refuse plain rm -rf / without an extra flag — but they won't save you from ~ or a wrong folder.

Files that start with a dash
A file named -old.txt jams the grammar from Getting Help: rm -old.txt reads as a fistful of flags, not a filename, and the error complains about an invalid option instead of the real problem. Two ways out: rm -- -old.txt — the double dash means "everything after this is a name, no matter what it looks like," and nearly every command honors it — or point at the file with a path, rm ./-old.txt. Worth knowing before some download names itself something dashed.

The Ritual: ls First, Then rm

Here's the habit that makes rm boring instead of scary. Never aim rm at anything you haven't just looked at. List first, confirm with your eyes, then recall the command (up-arrow) and swap ls for rm — same targets, guaranteed:

Look, confirm, then delete
ls old-project          # 1. LOOK: what exactly is in there?
pwd                     # 2. CONFIRM: am I where I think I am?
rm -r old-project       # 3. Same target you just inspected — up-arrow, edit, Enter
Important
The AI angle — this is a stop-and-read moment. When a coding agent proposes a command containing rm -rf, do not rubber-stamp it. Read three things before approving: the exact path (absolute? relative to what?), any wildcard (what could it expand to?), and the working directory the agent is running in. Agents make location mistakes exactly the way beginners do — they just make them faster. Deletion is the one category of command you always read in full — and Terminal for the AI Era later turns that instinct into a complete auditing method.
Tip
Training wheels while you learn: rm -i asks before every single deletion, and rm -I (capital i) asks once when deleting more than three files — a good permanent default. And for anything precious, the calmer move is often mv: relocate to a scrap folder today, delete the scrap folder next week. The exception is build artifacts — whatever the project's build leaves behind, the step that turns your source files into something you can run. Delete those freely; running the build again writes them straight back.

Try It: Clean the Downloads Mess

A cluttered downloads folder awaits: inspect what's there with ls and cat, make folders, sort keepers into them with mv, and rm the junk — practicing the ls-first ritual where deletion can't hurt you.
Clean the Downloads Mess

Loading playground...

Vibe it

"You suggested rm -rf — walk me through exactly what that path resolves to before I approve it"

"Delete the build artifacts in this project, but list everything you plan to remove first"

3.4 Wildcards

Everything so far has operated on files one name at a time. Wildcards — also called globs — let one pattern stand for many names: "every .log file," "all the photos from January." This is where selecting files by hand in a file manager starts to feel slow.

A glob is a net: the shell casts it over the directory and hands your command whatever it catches
Note
The mess: The agent's test run left 40 .tmp files littered through this folder. Deleting them one by one would take ten minutes. One pattern does it in a second — if you can trust what the pattern matches.
PatternMatchesExample
*Any run of characters (including none)*.logapp.log, errors.log
?Exactly one characterpage?.htmlpage1.html, not page12.html
[abc]One character from the set (ranges like [0-9] work too)report-[12].txtreport-1.txt, report-2.txt

The Big Idea: the Shell Expands the Glob, Not the Command

This is the one concept that makes wildcards predictable instead of magical. When you type rm *.tmp, the rm program never sees a star. The shell expands the pattern against the current directory first, then runs the command with the resulting list of plain filenames:

Two consequences follow directly. First, what a glob matches depends entirely on where you are — the same *.tmp is three files in one folder and three hundred in another (this is the wildcard half of the rm -rf disaster from Deleting (Carefully)). Second, since expansion happens before any command runs, you can ask a harmless command to show you the expansion:

The habit: echo the glob first
echo *.tmp
# cache1.tmp cache2.tmp scratch.tmp    <- exactly what rm would receive

rm *.tmp                               # Now you KNOW what this deletes
Important
Echo the glob first. echo just prints its arguments — so echo <pattern> is a free, safe preview of any wildcard. (ls <pattern> works too; the angle brackets mark the spot you fill in and are never typed — the convention from Getting Help.) Make it a reflex before every destructive glob, and apply it to AI-proposed commands: when an agent suggests a command containing a wildcard, echo that pattern in the target directory before you approve the real thing.

Globs compose with everything you already know — they're just a way of writing file lists:

Wildcards with cp, mv, ls
cp *.md drafts/           # Copy every Markdown file into drafts/
mv photo-0?.jpg january/  # Move photo-01.jpg through photo-09.jpg
ls report-[12].txt        # List just report-1.txt and report-2.txt
ls src/*.js               # Globs work inside a path too — but only one folder deep

That last line is where globs stop: one never crosses a /. src/*.js looks inside src and gives up there — it will not find src/lib/util.js, and no arrangement of stars will make it. When a mess is spread down through subfolders the pattern is the wrong tool, and walking a whole tree is find's job: find . -name '*.tmp' would have caught those 40 files wherever they'd landed. It has a section waiting in Finding Files.

Warning
Two classic surprises: * does not match hidden dotfiles (a small mercy — rm * spares your .env), and a pattern that matches nothing is passed to the command literally, star and all — which is why a typo'd glob often produces the baffling error cannot access '*.tpm'. Both surprises are caught instantly by the echo habit.
Braces look like a glob — and aren't one
One more pattern-shaped thing, so it never fools you: mkdir -p src/{components,lib}. Those braces don't match anything — they expand, unconditionally: the shell rewrites the line into mkdir -p src/components src/lib before mkdir ever runs. A glob asks "what's already here?"; braces say "make me these." That's why braces work for files that don't exist yet — exactly where a glob would come back empty-handed — and why coding agents lean on this shape every time they scaffold a project. You'll read it far more often than you'll type it.

Try It: Select Exactly the Right Files

A folder full of similar names — your job is to write patterns that catch exactly the right ones and nothing else. echo every glob before you commit to it; that's the skill being tested.
Select Exactly the Right Files

Loading playground...

Vibe it

"Write a glob that matches all the 2024 log files but not the 2025 ones, and echo it first"

"Move every image file in this folder into photos/ — show me what the wildcard matches before moving"

Challenge: Clean Up After the Agent

Loading challenge...