2 of 8

Linux FilesystemLesson 2 of 8

Directories: build, reshape, remove

Create whole trees in one command, copy and move them, and remove them without losing anything you meant to keep.

  • mkdir
  • rmdir
  • cp
  • mv
  • rm
  • find

25 min5 exercises

After this lesson you can

  • Create nested directories in one command, including from a pattern.
  • Copy, rename and move whole trees.
  • Remove directories safely, and know what rm -r will and will not ask.

Make directories

mkdir creates one directory. If the parent does not exist it refuses:

cd ~/lab
mkdir projects/web
mkdir: cannot create directory ‘projects/web’: No such file or directory

-p creates every missing parent, and does nothing if the directory already exists. That makes it safe to run twice, which is why scripts use it everywhere:

mkdir -v -p projects/web/css projects/api
mkdir: created directory 'projects'
mkdir: created directory 'projects/web'
mkdir: created directory 'projects/web/css'
mkdir: created directory 'projects/api'

-v (verbose) prints what it did. Without -p, an existing directory is an error:

mkdir projects/web/css
mkdir: cannot create directory ‘projects/web/css’: File exists

Build a whole layout from a pattern

The shell expands braces before the command runs, so one command can create a whole layout:

mkdir -p demo/{src,docs,tests}
ls demo
docs  src  tests

echo shows what the shell will pass, which is a good habit before anything destructive:

echo demo/{src,docs,tests}
demo/src demo/docs demo/tests

See a tree

tree is not installed everywhere. find always is, and ls -R is close:

ls -R projects
projects:
api  web

projects/api:

projects/web:
css

projects/web/css:
find projects -type d | sort
projects
projects/api
projects/web
projects/web/css

-type d keeps directories, -type f keeps files. To see only the top level, add -maxdepth 1. To list the directories in the current location, ls -d */ also works.

Copy a directory

cp refuses a directory unless you ask for recursion:

touch projects/web/index.html projects/web/css/site.css
cp projects/web projects/web2
cp: -r not specified; omitting directory 'projects/web'
cp -r projects/web projects/web-backup
find projects/web-backup | sort
projects/web-backup
projects/web-backup/css
projects/web-backup/css/site.css
projects/web-backup/index.html

If the target already exists, cp -r source target puts source inside it as target/source. If it does not exist, the copy becomes target. Check which one you have before you run it.

Move and rename

mv does both jobs, because renaming is moving within the same directory:

mv projects/web-backup projects/archive
mv projects/archive/index.html projects/archive/home.html
find projects/archive | sort
projects/archive
projects/archive/css
projects/archive/css/site.css
projects/archive/home.html

mv silently overwrites an existing target file. -n refuses to, and -i asks first:

mv -n projects/web/index.html projects/archive/home.html
mv: not replacing 'projects/archive/home.html'

Moving inside one filesystem only rewrites a directory entry, so it is instant for a tree of any size. Across filesystems it has to copy and then delete.

Remove directories

Two commands, two levels of caution:

rmdir projects/api

rmdir only removes an empty directory and fails otherwise:

rmdir projects/archive
rmdir: failed to remove 'projects/archive': Directory not empty

rm on a directory needs -r:

rm projects/archive
rm: cannot remove 'projects/archive': Is a directory
rm -r projects/archive

There is no recycle bin. A removed tree is gone. Make it a habit to look before you remove:

ls projects/archive
rm -r projects/archive

Remove a chain of empty parents with -p:

mkdir -p a/b/c
rmdir -p a/b/c

It stops at the first directory that is not empty.

Names with spaces

The shell splits on spaces, so an unquoted name becomes several arguments:

echo x > "my file.txt"
rm my file.txt
rm: cannot remove 'my': No such file or directory
rm: cannot remove 'file.txt': No such file or directory

Quote the name, or escape each space:

rm "my file.txt"
rm my\ file.txt

The best habit is to avoid spaces in names you create, and to quote every name you did not create.

Don’t remove the directory you are standing in

Linux lets you:

mkdir -p ~/lab/x/y
cd ~/lab/x/y
rmdir ~/lab/x/y
pwd
/home/alex/lab/x/y

The shell still believes it is there, but the directory is gone, and creating files in it fails. Run cd ~ to recover.

Practice

0 of 5 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    Build a tree in one command

    Make ~/lab/site with three subdirectories, css, js and img, using a single mkdir command. Then list every directory below site.

    Check your result
    find ~/lab/site -type d | sort
    /home/alex/lab/site
    /home/alex/lab/site/css
    /home/alex/lab/site/img
    /home/alex/lab/site/js
    Hint

    -p creates parents. Braces expand to several names: {a,b}.

    Solution
    mkdir -p ~/lab/site/{css,js,img}
  2. Warm-up

    Read the error, then fix it

    Run mkdir ~/lab/docs/2026/notes. It fails. Say why in one sentence, then create the directory with one command.

    Check your result

    The first command prints mkdir: cannot create directory '/home/alex/lab/docs/2026/notes': No such file or directory, because docs and 2026 do not exist yet. After the fix, this prints the path:

    ls -d ~/lab/docs/2026/notes
    Hint

    The parents are missing.

    Solution
    mkdir -p ~/lab/docs/2026/notes
  3. Practice

    Copy a tree and rename the copy

    Copy ~/lab/site to ~/lab/site-backup, then rename the copy to ~/lab/archive-2026. Prove the two trees have the same structure with a command that prints nothing when they match.

    Check your result
    diff -r ~/lab/site ~/lab/archive-2026 && echo identical
    identical
    Hint

    cp needs a flag to copy directories. mv renames when the target does not exist.

    Solution
    cp -r ~/lab/site ~/lab/site-backup
    mv ~/lab/site-backup ~/lab/archive-2026
    diff -r ~/lab/site ~/lab/archive-2026 && echo identical
  4. Practice

    Remove only the empty directories

    Create the setup below. Then remove every empty directory under ~/lab/scratch with one command, without touching full or the file inside it.

    mkdir -p ~/lab/scratch/{empty/deeper,full}
    touch ~/lab/scratch/full/keep.txt
    Check your result
    find ~/lab/scratch | sort
    /home/alex/lab/scratch
    /home/alex/lab/scratch/full
    /home/alex/lab/scratch/full/keep.txt
    Hint

    find can select empty directories and delete what it selects. It deletes children before parents, so empty is empty by the time it is reached.

    Solution
    find ~/lab/scratch -type d -empty -delete
  5. Challenge

    Move a tree whose names contain spaces

    Create ~/lab/Q4 Report/2026 plan/budget.txt (names contain spaces). Move the whole Q4 Report tree into ~/lab/archive/ by copying it first and removing the original only after you have checked the copy. The original must be gone at the end and the file must exist in the archive.

    Check your result
    find ~/lab/archive -name 'budget.txt'
    ls ~/lab | grep -c 'Q4 Report'
    /home/alex/lab/archive/Q4 Report/2026 plan/budget.txt
    0
    Hint

    Quote the names, or escape each space with a backslash. Make archive before you copy into it.

    Solution
    mkdir -p "$HOME/lab/Q4 Report/2026 plan" "$HOME/lab/archive"
    touch "$HOME/lab/Q4 Report/2026 plan/budget.txt"
    cp -r "$HOME/lab/Q4 Report" "$HOME/lab/archive/"
    find "$HOME/lab/archive" -name budget.txt
    rm -r "$HOME/lab/Q4 Report"