insideout Get Pro

01 · Files and folders in the shell

Free

Engineering Toolkit, lesson 1

Files and folders in the shell

Find your way around, look inside files, and move them about safely.

Git, Python projects, databases and containers are all driven from a terminal, and the rest of this module uses one. This lesson gets you around it: where you are, what's inside a file, and how to move files about without losing any. You need a computer and nothing else.

By the end, you can:

  1. Read a path, and move between folders with cd.
  2. Check a file's first lines, last lines and length without opening it.
  3. Make, copy, rename and delete files and folders.
  4. Predict which files a wildcard matches, before you delete anything.

Part 1. Where you are, as a path

Read a path, and move between folders with cd.

Open a terminal: Terminal on a Mac, any terminal on Linux, or WSL or Git Bash on Windows. The line ending in $ is the prompt. The shell, the program that reads your commands, always sits in one folder. pwd prints which one, ls lists what's in it, and cd moves you somewhere else.

Moving around
$ pwd
/home/ana
$ ls
notes  projects
$ cd projects/textstats
$ ls
count.py  data  README.md
$ cd ../../notes
$ pwd
/home/ana/notes
$ cd
$ pwd
/home/ana

Example 1.1

Following the session

A new terminal runs the commands in this session. Where is the shell after each cd, and why?

  1. Start at homepwd → /home/ana

    A new terminal starts in your home folder. On a Mac it would be /Users/ana.

  2. Go down two folderscd projects/textstats

    A relative path: projects is looked for inside /home/ana, then textstats inside projects.

  3. Go up two, then downcd ../../notes → /home/ana/notes

    Each .. climbs one level, from textstats to projects to ana. Then notes is looked for there.

  4. Go homecd → /home/ana

    cd on its own goes to your home folder, and so does cd ~.

Answer/home/ana/projects/textstats, then /home/ana/notes, then home again.

A path is a route through folders, read one name at a time. If it starts with /, the route starts at the top of the disk, so it means the same thing from anywhere. If it doesn't, it starts where you are. That's why ../../notes works from textstats, but would land somewhere else from any other folder.

/hometmpananotesprojectstextstats....you are herehome, ~From textstats../../notesup, up, then downFrom anywhere/home/ana/notesstarts at the top, /
/hometmpananotesprojectstextstats....you are herehome, ~From textstats../../notesFrom anywhere/home/ana/notes
Figure 1The folders from the session. From textstats, ../../notes climbs to projects, then to ana, then goes down to notes. /home/ana/notes reaches the same folder from anywhere.

Working directory and path

The working directory is the folder the shell is in now, which pwd prints. A path names a file or folder by its route. It's absolute if it starts with /, and relative to the working directory if not. .. means one level up, and ~ your home folder.

../../notes from textstats = /home/ana/notes from anywhere

On a Mac, home is /Users/ana and the shell is zsh, the default since macOS 10.15. Most Linux systems use bash. The two behave alike for everything here, and where their output differs, the lesson says so. To save typing, type the first letters of a name and press Tab: the shell finishes it.

Your turn

You're in /home/ana/projects/textstats/data. You type cd ../../../notes. Where are you now?

Three .. climb from data to textstats, to projects, to /home/ana. Then notes is looked for there: /home/ana/notes. Counting one level too few gives /home/ana/projects/notes, and one too many gives /home/notes.

Part 2. Looking inside a file

Check a file's first lines, last lines and length without opening it.

You rarely need an editor just to see what a data file holds. From inside the textstats folder, three commands answer the first questions: how does it start, how does it end, and how long is it?

First lines, last lines, line count
$ cd projects/textstats
$ head -n 3 data/reviews.txt
Great kettle, boils fast and looks good.
Stopped working after a week.
Fine for the price.
$ tail -n 2 data/reviews.txt
Returned it, the switch was faulty.
Good value, solid build.
$ wc -l data/reviews.txt
12 data/reviews.txt

Example 2.1

A file of reviews

data/reviews.txt holds one product review per line. What are its first three reviews, its last two, and how many are there?

  1. Read the first lineshead -n 3 → 3 reviews

    head prints the start of a file. -n 3 asks for 3 lines; without it you get 10.

  2. Read the last linestail -n 2 → 2 reviews

    tail prints the end of a file, also 10 lines unless you ask for a different number.

  3. Count the lineswc -l → 12

    wc counts lines, words and bytes, and -l keeps only the lines. On a Mac it pads the number with spaces.

Answer12 reviews, from "Great kettle" to "Good value, solid build."

-n 3 and -l are options. They change what a command does, and usually come before the file it works on. Almost every command takes some, and man head lists all of head's (press q to leave). On Linux, head --help prints a shorter list.

Flag

An option after a command's name that changes what it does, starting with a dash, like -n 3 or -l. Many also have a long form with two dashes, like --lines=3.

head -n 3 data/reviews.txt: a command, a flag, a file

wc -l counts newline characters, so a file whose last line has no newline after it comes out one line short. To read a whole file, less data/reviews.txt shows it a screen at a time: Space for the next page, /kettle to search, q to quit.

For a log that's still being written, tail -f keeps printing new lines as they arrive. It never ends by itself: press Ctrl-C to stop it and get your prompt back.

Your turn

A file has 5,000 lines. Which command prints its last 10, lines 4,991 to 5,000?

tail prints the last 10 lines unless you ask for another number, so tail big.txt and tail -n 10 big.txt do the same. tail -n 4991 prints the last 4,991 lines, head prints the first 10, and wc -l only counts them.

Part 3. Copying, moving and deleting

Make, copy, rename and delete files and folders.

Four commands do the housekeeping: mkdir makes a folder, cp copies, mv moves or renames, and rm deletes. Here they back up the script and its data, rename the backup, then throw it away.

Housekeeping
$ mkdir -p backup/2026-11
$ cp count.py backup/2026-11/
$ cp -R data backup/2026-11/
$ ls backup/2026-11
count.py  data
$ mv backup archive
$ ls
archive  count.py  data  README.md
$ rm -r archive
$ ls
count.py  data  README.md

Example 3.1

A backup, renamed, then deleted

Copy count.py and the data folder into backup/2026-11, rename backup to archive, then delete it.

  1. Make the foldersmkdir -p backup/2026-11

    -p makes backup first, then 2026-11 inside it, and doesn't complain if they already exist.

  2. Copy a filecp count.py backup/2026-11/

    When the last name is a folder, the copy goes inside it with the same name.

  3. Copy a foldercp -R data backup/2026-11/

    -R copies the folder and everything in it. Without it, cp refuses.

  4. Renamemv backup archive

    mv to a new name in the same place is a rename. Everything inside goes along.

  5. Deleterm -r archive

    -r deletes the folder and everything in it, in one go.

AnswerThe folder is back to count.py, data and README.md.

A folder holds files and other folders, so copying or deleting one means going through everything inside it, at every level. Commands only do that when a flag asks them to. Without one, cp and rm refuse, and say data is a directory, the shell's word for a folder.

Recursive

Done to a folder and everything inside it, at every level. cp -R copies a folder this way and rm -r deletes one. cp -r works too, but the Mac's manual for cp advises -R.

cp -R data backup/ copies a folder, rm -r backup deletes one

rm has no bin: deleted is deleted. cp and mv also replace a file of the same name without asking. Add -i and each of the three asks first, one file at a time. Before any rm -r, run ls on the same path and read what it lists.

Your turn

You're in a folder that holds results, a folder of CSV files. What does cp results results-old do?

Without -R, cp won't copy a folder. On Linux it says -r not specified; omitting directory 'results'. cp -R results results-old makes the copy. Renaming is mv's job, and it would leave no copy behind.

Part 4. Wildcards expand before the command runs

Predict which files a wildcard matches, before you delete anything.

A * in a name matches any run of characters, even none, and ? matches exactly one. Wildcards save typing, and they're also how people delete the wrong files. In this session the data folder holds four files.

What a wildcard matches
$ ls data
access.log  reviews.txt  scores.csv  scores.csv.bak
$ echo data/*.csv
data/scores.csv
$ echo data/scores*
data/scores.csv data/scores.csv.bak
$ rm data/scores*
$ ls data
access.log  reviews.txt

Example 4.1

What rm data/scores* deletes

Before you run rm data/scores*, find out exactly which files it will delete.

  1. Print the pattern firstecho data/scores*

    echo prints whatever it's given, and by then the shell has already swapped the pattern for the names it matches.

  2. Read the matchesdata/scores.csv data/scores.csv.bak

    Both names start with scores. * doesn't care what follows, so the .bak file matches too.

  3. Run the real commandrm data/scores*

    rm receives those same two names. It never sees the *, and it deletes both without asking.

AnswerBoth scores files. access.log and reviews.txt stay.

The shell, not the command, works out what a pattern matches. It swaps the pattern for the matching names, in alphabetical order, and only then starts the command. So every command takes wildcards, and echo with the same pattern shows exactly what another command would receive.

You typerm data/scores*The shell looks in data, before rm startsaccess.logscores.csvreviews.txtscores.csv.bak2 names start with scoresrm runs with two plain namesrm data/scores.csv data/scores.csv.bak
Figure 2You type a pattern. The shell looks in data, swaps the pattern for the two names that match, and only then runs rm.

Wildcard

A * (any characters, or none) or ? (exactly one character) in a file name. The shell replaces the pattern with every name that matches, in alphabetical order, before the command runs.

echo data/scores* first, then rm data/scores*

Two surprises. * skips names that start with a dot, such as .env, unless the pattern starts with a dot too. And when nothing matches, bash passes the pattern on unchanged, so ls *.json looks for a file called *.json. zsh, the Mac's shell, stops instead: zsh: no matches found: *.json.

Your turn

The folder data has scores.csv, scores.csv.bak and reviews.txt in it. Which files does rm data/*.csv delete?

*.csv matches names that end in .csv. scores.csv.bak ends in .bak, so it survives, and reviews.txt doesn't match at all. -r is only about folders. Wildcards work with any command, because the shell expands them first.

Where you'll meet it

On the job

When a training run or a service misbehaves on a remote server, often all you have is a shell over SSH. cd to the logs, ls to find them, wc -l to see how big they are, tail -n 50 to read the end and tail -f to watch new lines arrive. Those few commands cover most first looks.

Check yourself

Four questions from across the lesson. Try each one before you look back.

1

You're in /home/ana/projects, and /home/ana has a folder called notes. You type cd ../notes. Where are you now?

.. takes you up to /home/ana first, and then notes is looked up from there. A relative path is read one step at a time from where you stand, so it can go up and then down again.

2

A log file has two million lines, and you want to see the latest entries. What do you run first?

New entries are added at the end of a log, so tail shows the latest. cat pours all two million lines onto your screen, head shows the oldest entries, and a file that size can be slow to open in an editor. tail -f app.log goes one better: it keeps printing new lines as they're written.

3

A folder holds .env, notes.txt and todo.txt. What does echo * print there?

* skips names that start with a dot unless the pattern starts with one too, so .env stays out, just as a plain ls hides it. ls -a shows it.

4

A command has been running far longer than it should, and you want your prompt back. What do you press?

Ctrl-C asks the running program to stop, and almost every program does. Ctrl-Z only pauses it: it's still there holding its memory until you resume it with fg or end it. Typing exit goes to the running program, not to the shell. Closing the window works, but it also ends everything else that terminal was running.

What to remember

  1. 1Where you are, as a path

    A path that starts with / is absolute and works from anywhere; any other path starts from the working directory, and each .. climbs one level, so cd ../../notes from textstats reaches /home/ana/notes.

  2. 2Looking inside a file

    head -n 3 prints a file's first 3 lines, tail -n 3 its last 3, and wc -l counts its lines; with no -n, head and tail print 10.

  3. 3Copying, moving and deleting

    Folders need a flag to copy or delete, cp -R and rm -r, and rm has no bin: deleted is deleted.

  4. 4Wildcards expand before the command runs

    The shell expands a wildcard before the command runs, so echo data/scores* shows exactly what rm data/scores* would delete.

Recap and going further

The lesson on one page

Where you are
  • pwd prints the working directory
  • ls lists it, cd moves
  • Tab finishes a name
Paths
  • Absolute paths start at /
  • Relative paths start where you are
  • .. is up one, ~ is home
Look inside
  • head and tail: 10 lines unless -n
  • wc -l counts newlines
  • tail -f follows a log; Ctrl-C stops it
Get around in the shell
Housekeeping
  • mkdir -p, cp -R and mv
  • rm -r has no bin and no undo
  • -i asks before replacing or deleting
Wildcards
  • The shell expands * before the command runs
  • * skips names that start with a dot
  • echo a pattern before you rm it

Try this

Find your way around a real project

Open a terminal and go to a project you know, such as a folder of notebooks, using cd and Tab. List it with ls -a. Find its biggest data file, count its lines with wc -l and read its first rows with head. Then make a scratch folder, copy a few files into it with cp, echo a wildcard to see what it matches, and delete the scratch folder with rm -r.

You need: A terminal: Terminal on a Mac, any terminal on Linux, or WSL or Git Bash on Windows.

Look for

  • The files ls hides until you add -a, such as .git or .env.
  • How pwd changes as you cd .. and come back.
  • What echo *.csv prints in a folder with no CSV files: bash and zsh answer differently.
  • Whether wc -l on a CSV gives one more than the rows your notebook shows, and why: the header line.

The words, in plain language

Working directory and path
The working directory is the folder the shell is in now, which pwd prints. A path names a file or folder by its route. It's absolute if it starts with /, and relative to the working directory if not. .. means one level up, and ~ your home folder.
Flag
An option after a command's name that changes what it does, starting with a dash, like -n 3 or -l. Many also have a long form with two dashes, like --lines=3.
Recursive
Done to a folder and everything inside it, at every level. cp -R copies a folder this way and rm -r deletes one. cp -r works too, but the Mac's manual for cp advises -R.
Wildcard
A * (any characters, or none) or ? (exactly one character) in a file name. The shell replaces the pattern with every name that matches, in alphabetical order, before the command runs.

Engineering Toolkit overviewSpotted a mistake? Tell us.