Skip to content

Getting started · Module 1

The command line

build · 260 minVerified 21 Sept 2026

This module teaches you to control a computer by typing commands instead of clicking. Every later module in this course runs Python from this kind of window, so it comes first. You will install Python from it, run your programs from it, create the folders for your expense tracker from it, and read error messages in it.

You need nothing before this module except a computer you can use. No earlier module is required.

After this module you will be able to:

  • open a terminal and recognise which shell is running inside it;
  • move between folders and always know which folder you are in;
  • create folders, including folders with spaces in their names;
  • run a program and find out whether it succeeded, by reading its exit status;
  • read and set environment variables, and explain how PATH decides which program runs.

The next module, “Installing Python and an editor”, uses all five of these skills.

Nothing on this page assumes you have used a terminal before. Commands are shown without the prompt text that appears in front of them, so you type only what is in the grey box. Where macOS and Linux differ from Windows, the page shows both in tabs. Pick your system once and the other tabs on the page follow your choice.

Attempt first15 min · hard stop

Fifteen minutes, then stop, wherever you are. Get as far as you can. You are expected to get stuck. The places where you get stuck are the questions the rest of this page answers.

Allowed: official reference documentation, such as the man pages on macOS and Linux, Get-Help in PowerShell, the GNU Bash manual and Microsoft’s PowerShell documentation. Not allowed: the rest of this page, your notes, any AI assistant, and tutorials found by searching.

Do these seven things, in order:

  1. Open a terminal window.
  2. Find out, by typing a command, which folder the terminal is currently working in. Write down the full answer it prints.
  3. Inside your home folder, create a folder named projects. Inside projects, create a folder named expense-tracker.
  4. Move into expense-tracker. Then type a command that proves you are there.
  5. Run the program called whoami.
  6. Make the terminal print the exit status of whoami. The exit status is the number a program reports when it finishes.
  7. Type one command that you expect to fail. Print its exit status too.

While you work, write down every confusion as it happens. Copy every error message exactly as the terminal printed it, including any numbers. That record is what you will use in ## Open loops.

Show solutionHide solution

Here is one complete way to do all seven steps. Choose the tab for your system.

Terminal window
pwd
cd ~
mkdir projects
cd projects
mkdir expense-tracker
cd expense-tracker
pwd
whoami
echo $?
ls missing.txt
echo $?

What each line does, from top to bottom:

  • pwd prints the folder the terminal is working in. The name stands for “print working directory”.
  • cd ~ moves you to your home folder. cd means “change directory”, and ~ is a short name for your home folder.
  • mkdir projects creates a folder named projects in the current folder. mkdir means “make directory”. If projects already exists, you get an error, and you can move on to the next line.
  • cd projects moves into the new folder.
  • mkdir expense-tracker and cd expense-tracker do the same one level deeper.
  • The second pwd proves where you are. The last part of the path it prints is expense-tracker.
  • whoami is a small program that prints your user name.
  • echo $? prints the exit status of the command that ran just before it. In PowerShell, $LASTEXITCODE prints the exit status of the last program that is a separate file, such as whoami. 0 means the program succeeded.
  • ls missing.txt asks for a file that does not exist, so it fails.
  • echo $? then prints a number other than 0. On Linux the number is 2. In PowerShell, $? prints False, because PowerShell’s ls is one of its own commands and reports failure as False rather than as a number.

On the test machine, the Linux part of the last three lines printed this:

ls: cannot access 'missing.txt': No such file or directory
2

Every idea used here is explained, step by step, under ## How it works.

This section explains everything the attempt needed, in four parts. The first part covers what a terminal and a shell are, and how the shell reads what you type. The second covers folders, paths and the three commands for moving around. The third covers how the shell finds and runs a program, and how the program reports success or failure. The fourth covers environment variables, the settings every program receives from the shell.

This part explains the window you type into and the program that actually reads your commands. You need both ideas before any command makes sense. Every error message you will ever see comes from one of the programs described here.

Terminal, shell and prompt. Three words describe the text window you typed into during the attempt. They name three different things, and beginners often mix them up.

A terminal is the application that shows a window of text. It takes the keys you press and sends them on. It then shows any text that comes back. The terminal itself does not understand commands. On macOS the application is called Terminal. On Windows it is usually Windows Terminal or the PowerShell window. On Linux its name depends on your desktop.

A shell is the program that runs inside the terminal and understands commands. The shell reads the line you typed. It works out which program you want. It starts that program. When the program finishes, it waits for your next line. Different shells exist:

  • zsh is the default shell on macOS, starting with macOS Catalina.
  • bash is the shell most Linux systems start.
  • PowerShell is Microsoft’s shell, and the one this course uses on Windows.

A prompt is the short text the shell prints when it is ready for your next command. Its exact form differs between computers and can be changed, so do not rely on its appearance. It usually shows your user name, the computer name or the current folder, and it ends with a symbol such as $, %, # or >. In PowerShell the prompt starts with PS. On the Linux test machine it was PS /root>. On Windows the folder part looks like C:\Users\yourname.

Why the split exists. The terminal and the shell are separate so that one terminal can run any shell. It also means one shell works in any terminal. Windows Terminal, for example, was announced by Microsoft as one application for Command Prompt, PowerShell and the Windows Subsystem for Linux. The practical result for you is this: when a command misbehaves, the shell is almost always the program to investigate, not the window.

The diagram below shows what happens between pressing Enter and seeing the next prompt. It has four actors: you, the terminal, the shell, and the program you asked for.

sequenceDiagram
  participant You
  participant T as Terminal
  participant S as Shell
  participant P as Program whoami
  You->>T: type whoami, press Enter
  T->>S: pass the typed line
  S->>S: split the line into words
  S->>P: start the program
  P->>T: write its output
  T->>You: display the output
  P-->>S: finish with an exit status
  S->>T: print the next prompt

Read the diagram from top to bottom. The terminal only carries text in both directions. The shell does the thinking: it splits the line into words and starts the program. The program writes its output, and the terminal displays it. When the program finishes, it hands a number back to the shell. That number is the exit status, explained in the third part of this section. Only then does the shell print a new prompt.

How to open a terminal. Choose your system.

On macOS, open the Terminal application. It is in the Utilities folder inside Applications. You can also press Command and Space together, type Terminal, and press Return. The shell inside it is zsh unless your account was created before macOS Catalina or someone changed it.

On Linux, open the application called Terminal or a similar name from your applications menu. The shell inside it is usually bash.

How to tell which shell you are in. Error messages name the shell that printed them. Here is the same typing mistake in each shell, copied from the test machine:

bash: lss: command not found
zsh: command not found: lss
lss: The term 'lss' is not recognized as a name of a cmdlet, function, script file, or executable program.

The first line came from bash and starts with bash:. The second came from zsh and starts with zsh:. The third came from PowerShell, which uses the longer “is not recognized” sentence. You will use this later to confirm which shell a set of instructions was written for.

Worked example. Open a terminal and type whoami, then press Enter.

Terminal window
whoami

The shell reads the word whoami, finds a program with that name, and starts it. The program prints your user name. On the test machine the output was root, because the tests ran as the administrator account. On your computer you will see your own user name. On Windows the name includes the computer name, such as LAPTOP\yourname. Then the prompt appears again, which tells you the shell is waiting for your next line.

Common mistakes.

  • Copying the prompt along with the command. Instructions on the web often show $ whoami. The $ is the prompt, not part of the command. If you type it, the shell looks for a program named $ and fails. Type only what comes after the prompt.
  • Following instructions written for another shell. Instructions written for bash often fail in PowerShell, and the reverse. Before you follow a set of commands, check which shell they were written for. This page labels every difference.
  • Thinking the window is frozen. If no new prompt appears, the program you started is still running. It may be waiting for input. Pressing Ctrl and C together asks it to stop.

Where this connects. Every later module types commands into this shell. The next topic explains how the shell turns the line you typed into a program name and a list of words for that program.

Commands, arguments and options. A command is one line you type and send with Enter. Before running anything, the shell splits that line into separate words. It splits at unquoted spaces. The first word is the command name, which says which program to run. Every word after it is an argument, a piece of information passed to that program.

Why the split matters. A program does not see your line as you typed it. It receives a list of separate words. The GNU Bash manual describes this as the shell breaking the input into words, following its quoting rules, before it runs anything. So the number of spaces decides the number of words, and the number of words decides what the program is told to do.

An option is an argument that changes how a program behaves, instead of naming a thing for it to work on. By convention, options start with a hyphen. A single hyphen introduces a short, one-letter option, such as -a. Two hyphens introduce a long, whole-word option, such as --all. The program decides what each option means, not the shell. For example, the GNU ls manual lists -a and --all as two spellings of the same option, which shows hidden entries. PowerShell commands also use a hyphen, followed by a whole name, such as -ItemType.

The table below shows how the shell splits three lines. Read each row from left to right.

You type Command name Words passed to the program
ls -a -l notes ls -a, -l, notes
mkdir my project mkdir my, project
mkdir "my project" mkdir my project (one word)

The first row has two options and one ordinary argument. The second row is a trap. The space between my and project separates two words, so mkdir receives two names and creates two folders. The third row puts quotation marks around the name. Text inside quotation marks stays together as one word even if it contains spaces. So mkdir receives one name and creates one folder called my project. Single quotation marks, 'my project', work the same way in bash, zsh and PowerShell.

Many programs let you combine short options. On the test machine, ls -a -l notes and ls -la notes printed exactly the same thing. Whether this works depends on the program, not the shell.

How a program reacts to a word it does not understand. The program checks its own options. When it finds an option it does not know, it prints its own error message. Here is the output of whoami --nonsense on the test machine:

whoami: unrecognized option '--nonsense'
Try 'whoami --help' for more information.

This message starts with whoami:, not with bash:. That tells you the shell did its job: it found and started whoami. The program itself then rejected the option. Knowing which program printed a message tells you where the problem is.

How to look up a program’s options. Many programs print a summary of their options with --help, as the message above suggests. On macOS and Linux, man followed by a program name opens that program’s manual. For example, man ls opens the manual for ls, and you press q to leave it. In PowerShell, Get-Help followed by a command name shows its help, for example Get-Help Get-ChildItem.

Common mistakes.

  • Spaces in names. An unquoted space always splits a word. Quote any name that contains a space, or avoid spaces in the names you create. This course uses hyphens instead, as in expense-tracker.
  • Assuming an option means the same thing everywhere. Options belong to programs. -a means “show hidden entries” to ls on Linux. It may mean something else, or nothing, to another program.
  • Forgetting the space between the command and the argument. cd projects has two words. cdprojects is one word, which names a program that does not exist.

Where this connects. Python programs receive their arguments in exactly this way. The module “Using the Python interpreter” shows the list of words arriving in a Python program. The next part of this page uses arguments to tell cd, ls and mkdir which folder to work on.

This part explains how the terminal names files and folders, and why the same name can point to different places. It then teaches the three commands you will use in every later module: cd, ls and mkdir.

A folder is also called a directory. The two words mean the same thing. Terminals and manuals usually say directory. Files and folders are arranged as a tree. The single folder at the top of the tree is the root. Every other folder sits inside another folder, which is called its parent.

The working directory. The working directory is the one folder that the shell is “in” at any moment. It is also called the current directory. Every command you type runs inside that folder.

It exists so that you do not have to type the full location of every file. When you give a program a name such as notes.txt, the program looks for it in the working directory.

Every running program, which the operating system calls a process, has its own working directory. A program started from the shell receives a copy of the shell’s working directory when it starts. The Linux manual page on path resolution states both facts: the working directory is inherited from the parent, and it is changed with a system function called chdir.

When you open a new terminal, the shell usually starts in your home folder. The home folder is the folder the system gives your user account for your own files.

To see the working directory, type pwd. In PowerShell, pwd is a short name for the command Get-Location, so the same word works.

Terminal window
pwd

In bash and zsh, the output is one line holding the full location of the folder, such as /Users/yourname on macOS or /home/yourname on Linux. On the test machine it printed /root. In PowerShell, the output is a small table: a heading Path, a dashed line, and the location under it, such as C:\Users\yourname.

Common mistakes. The most common mistake is forgetting which folder you are in. A command that worked a minute ago fails, or creates something in the wrong place, because you moved. The fix is a habit: run pwd whenever you are unsure. It costs one second. The section ## What broke, and why shows a folder created inside itself because of this mistake.

Where this connects. Python programs you write later open files by name. Those names are looked up from the working directory of the Python process, which it received from your shell. The module “Reading and writing files” depends on this.

Absolute and relative paths. A path is the text that names where a file or folder is. There are two kinds.

An absolute path starts at the root of the tree and lists every folder down to the target. It points to the same place no matter where you are. On macOS and Linux, an absolute path starts with /, the root. On Windows, an absolute path starts with a drive letter, a colon and a backslash, such as C:\.

A relative path does not start at the root. The system starts from the working directory and follows the path from there. The Linux manual page states the rule directly: a path that starts with / is absolute and is looked up from the root; any other path is relative and is looked up from the current working directory. Microsoft’s documentation describes the same rule for Windows.

Both kinds exist because each solves a different problem. Absolute paths are exact but long. Relative paths are short, but their meaning depends on where you are.

The tree below is used for the examples that follow. It shows a Linux home folder named sam, with two folders that each hold a file called notes.txt.

  • Directory/
    • Directoryhome
      • Directorysam
        • Directoryprojects
          • Directoryexpense-tracker
            • notes.txt
        • Directorystudy
          • notes.txt

There are two different files named notes.txt in this tree. Their absolute paths are /home/sam/projects/expense-tracker/notes.txt and /home/sam/study/notes.txt. The relative path notes.txt points to the first file when the working directory is /home/sam/projects/expense-tracker. The same relative path points to the second file when the working directory is /home/sam/study. This is why the same name can mean different files depending on where you are.

Relative paths use two special names. . (one dot) means “this folder”. .. (two dots) means “the parent of this folder”. Every folder contains both entries. ls -a shows them, which the test machine confirmed by printing . and .. inside an empty folder. The shell also treats ~ (tilde) as a short name for your home folder, so cd ~ goes home from anywhere.

To follow a relative path, the system works one step at a time. Here is how the path ../study/notes.txt is followed when the working directory is /home/sam/projects:

  1. Start at the working directory, /home/sam/projects.

  2. Take the first part, ... Move to the parent folder, which is /home/sam.

  3. Take the next part, study. Look for a folder called study inside /home/sam. It exists, so move into it. You are now at /home/sam/study.

  4. Take the last part, notes.txt. Look for it inside /home/sam/study. The result is the file /home/sam/study/notes.txt.

If any step names something that does not exist, the lookup stops. The program then reports “No such file or directory”. That is the error you saw if a cd failed during the attempt.

On Windows. Windows paths use a backslash \ between folder names. An example is C:\Users\sam\projects. PowerShell also accepts the forward slash /, because Windows converts separators to backslashes when it normalises a path. Microsoft’s path documentation warns about one Windows trap. C:\Projects is absolute: it starts at the root of drive C. C:Projects, without the backslash, is relative: it is relative to the current folder on drive C. Microsoft calls mixing these up a common source of bugs.

Common mistakes.

  • Using a relative path from the wrong place. The path is correct, but the working directory is not the one you assumed. Check with pwd.
  • Typing / at the start by habit. /projects means a folder named projects at the root of the whole system, not in your home folder. You almost certainly meant projects or ~/projects.
  • Leaving out a folder in the middle of the path. Every folder between the start and the target must appear in the path.

Where this connects. From the next module on, you will open project folders, run scripts by path, and read paths in error messages. The module “File and directory access” builds paths from Python code using the same absolute and relative rules.

cd, ls and mkdir. Three commands do almost all of your moving around. cd changes the working directory. ls lists what is in a folder. mkdir creates a folder. The same three names work in bash, zsh and PowerShell, but in PowerShell they are short names for PowerShell’s own commands.

The table below shows each task, the command on each system, and what the PowerShell name stands for.

Task macOS and Linux Windows (PowerShell) In PowerShell, the name stands for
Show the working directory pwd pwd Get-Location
Change the working directory cd path cd path Set-Location
List a folder ls or ls path ls or ls path Get-ChildItem
List including hidden entries ls -a ls -Force Get-ChildItem -Force
Create a folder mkdir name mkdir name a command that runs New-Item
Create nested folders at once mkdir -p a/b/c mkdir a/b/c New-Item creates the missing parents

Microsoft’s alias documentation lists cd, ls and pwd as Windows-only short names for Set-Location, Get-ChildItem and Get-Location. On macOS and Linux, PowerShell does not use them for ls. On the test machine, typing ls in PowerShell on Linux ran the system’s own ls program instead.

cd in detail. cd followed by a path makes that path the new working directory. cd .. moves up one level. cd with no argument goes to your home folder in bash and zsh; the GNU Bash manual names the home folder as the default for cd. cd ~ goes home in all three shells.

cd has an unusual property. It is not a separate program. It is built into the shell, and in bash type cd prints cd is a shell builtin. It has to be built in because a program started by the shell receives a copy of the shell’s working directory. If cd were a separate program, it could change only its own copy. The shell’s working directory would stay the same, and nothing would appear to happen.

ls in detail. ls with no argument lists the working directory. ls followed by a path lists that path instead. Names that start with a dot are hidden unless you add -a on macOS and Linux. -l switches to a long format with one entry per line, showing permissions, owner, size and date.

mkdir in detail. mkdir followed by a name creates that folder inside the working directory. It fails if the folder already exists. It also fails if a parent folder in the path does not exist yet. On macOS and Linux, mkdir -p creates every missing folder along the path and does not complain if the folder already exists. In PowerShell, mkdir passes the path to New-Item. On the test machine, running PowerShell 7.6.6, New-Item created missing parent folders without any extra option.

Worked example. This sequence builds a small tree and moves around inside it. It uses the /tmp folder on the test machine; use any folder you like.

Terminal window
mkdir -p notes/2026
ls
cd notes/2026
pwd
cd ..
pwd
ls -la

Here is what happens, line by line. mkdir -p notes/2026 creates notes, then 2026 inside it, in one command. ls shows notes. cd notes/2026 follows a relative path two levels down. The first pwd proves the move; on the test machine it printed /tmp/demo/notes/2026. cd .. goes up one level, and the second pwd printed /tmp/demo/notes. The final ls -la on the test machine printed this:

total 12
drwxr-xr-x 3 root root 4096 Sep 21 09:32 .
drwxr-xr-x 3 root root 4096 Sep 21 09:32 ..
drwxr-xr-x 2 root root 4096 Sep 21 09:32 2026

Look at the last column first. It holds three entries: . (this folder), .. (the parent) and 2026. The d at the start of each line means the entry is a directory. The first line, total 12, is a size total that you can ignore for now.

Common mistakes.

  • Creating a folder inside the folder you meant to create. Running mkdir expense-tracker while you are already inside expense-tracker creates expense-tracker/expense-tracker. The command succeeds and prints nothing, so nothing warns you.
  • mkdir on a path whose parent does not exist yet, on macOS and Linux, without -p. The error message names the whole path, which can make it look as if the new folder is the problem. The missing piece is actually the parent.
  • Running mkdir again for a folder that already exists. On Linux this prints File exists and fails. The folder is not damaged.

Where this connects. The next module opens your expense-tracker folder in an editor. The module “Virtual environments and packages” creates a folder inside it with a command, and you will need to be in the right folder when you run that command.

This part explains what the shell does after it has split your line into words. It covers how the shell finds the program you named, and how that program tells the shell whether it succeeded.

Running a program from the terminal. A program is a file the computer can execute. Programs such as ls, whoami and later python are files stored in folders on disk. When you type a command name, the shell has to find the file that belongs to that name.

The GNU Bash manual describes the search in order:

  1. If the name contains a slash, the shell does no search. It runs the file at that exact path.
  2. If there is no slash, the shell first checks its own definitions. A builtin is a command built into the shell itself, such as cd. A shell function is a small command defined inside the shell. If the name matches either one, the shell handles it itself.
  3. Otherwise, the shell searches each folder listed in the environment variable PATH, in order, for an executable file with that name.
  4. If the search finds nothing, the shell prints an error and sets the exit status to 127.

PATH is a list of folders stored as one line of text. On macOS and Linux the folders are separated by colons, :. On Windows they are separated by semicolons, ;. Environment variables are explained in full in the next part of this page. For now, treat PATH as the list of folders where the shell looks for programs.

The diagram below shows the same search as a decision path. It starts from the command name the shell took from your line.

flowchart TD
  A["Command name"] --> B{"Contains a slash?"}
  B -- yes --> R["Run that exact file"]
  B -- no --> C{"Builtin or function?"}
  C -- yes --> D["Shell does it itself"]
  C -- no --> E["Search PATH folders in order"]
  E --> F{"Found?"}
  F -- yes --> G["Run the first match"]
  F -- no --> H["command not found, status 127"]

Follow the arrows from the top. Only a name with no slash is looked up. The search through PATH happens only after the shell has checked its own commands. The search stops at the first folder that contains a match. So if two folders in PATH both hold a program with the same name, the folder listed earlier wins. This matters in the next module, when more than one Python may be installed on your computer.

PowerShell follows the same idea with its own names. It has built-in commands called cmdlets, such as Get-ChildItem, and short alternative names for them called aliases, such as ls. When a name is none of its own commands, PowerShell searches the folders in PATH.

How to find out which file a name runs. In bash, type answers the question. In PowerShell, Get-Command does.

Terminal window
type cd
type ls
echo $PATH

On the test machine, the bash lines printed cd is a shell builtin and ls is hashed (/usr/bin/ls). “Hashed” means bash remembered where it found ls last time, so it does not repeat the search. The GNU Bash manual calls this memory a hash table. echo $PATH printed the folders separated by colons, ending with /usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin. The folder /usr/bin is in that list, which is why typing ls found /usr/bin/ls.

In zsh, type works the same way. In PowerShell, Get-Command whoami shows the file it will run, in its Source column. $env:PATH -split ';' prints the PATH folders one per line, splitting the text at each semicolon.

You can also skip the search entirely by giving a path. On the test machine, /usr/bin/ls ran the same program as ls, because the name contains a slash.

Common mistakes.

  • A typing mistake in the command name. The shell finds no such program and reports “command not found” (bash and zsh) or “is not recognized” (PowerShell).
  • A program that is installed but not in PATH. The file exists, but the shell never looks in its folder. The result is the same error as a typing mistake. This happens often with Python on Windows, and the next module shows how to check for it.
  • Changing PATH by replacing it instead of adding to it. After that, almost nothing runs. ## What broke, and why reproduces this.

Where this connects. In the next module you will type python --version. That line is found in exactly this way: the shell searches PATH for a program called python.

Exit status. When a program finishes, it gives the shell a whole number called its exit status. The exit status is also called the exit code or return status. 0 means success. Any other number means failure.

The number exists because output text is written for people, not for other programs. The number gives scripts and tools a reliable way to ask “did that work?”. The GNU Bash manual explains why zero is used for success: there is one clear way to succeed and many different ways to fail, so each failure can have its own number.

A few numbers have fixed meanings in bash:

  • 127 means the command was not found.
  • 126 means a file was found but could not be executed.
  • Other numbers are chosen by each program. For example, GNU ls documents 1 for minor problems and 2 for serious trouble, such as a path given on the command line that does not exist.

How to print the exit status. Bash and zsh store the exit status of the last command in a special parameter called $?. echo $? prints it. PowerShell uses two variables:

  • $? holds True if the last command succeeded and False if it failed. It works after every command, including PowerShell’s own cmdlets.
  • $LASTEXITCODE holds the number from the last native program. A native program is a separate program file, such as whoami. Microsoft’s documentation states that after a native program, $? is True exactly when $LASTEXITCODE is 0.

This is the complete example on each system: one command that succeeds, then one that fails, printing the status after each.

Terminal window
whoami
echo $?
ls missing.txt
echo $?

On the test machine, the bash version printed this:

root
0
ls: cannot access 'missing.txt': No such file or directory
2

Here is how to read it. whoami printed the user name, and its status was 0, so it succeeded. ls printed an error message and its status was 2, which is its documented number for a path it cannot access. The exact wording of the ls message comes from GNU ls on Linux. macOS has a different ls program, so the words and the number may differ there, but the number will not be 0.

In PowerShell, ls missing.txt runs Get-ChildItem. On the test machine it printed Get-ChildItem: Cannot find path '/tmp/demo/missing.txt' because it does not exist., and $? then printed False. On Windows the path in that message is a Windows path.

The exit status is replaced by every command. $? always describes the command that ran immediately before it. That includes echo itself. So if you print the status twice in a row, the second print shows 0: it reports that the first echo succeeded. Copy the value right away, or print it only once.

Common mistakes.

  • Reading the status too late. Any command in between replaces it.
  • Using $LASTEXITCODE after a PowerShell cmdlet. Cmdlets do not set it, so it still holds the number from some earlier native program. Use $? after cmdlets.
  • Deciding from the text alone. Some programs print warnings and still succeed. The exit status is the program’s own verdict, so check it when it matters.

Where this connects. Your Python programs will set an exit status when they finish. The module “Command-line interfaces: sys.argv and argparse” teaches how to choose that number. Automated tests, taught in the module “Testing”, report failure through the exit status too.

This part explains the named settings every program receives from the shell. PATH is one of them, and it decides which programs run. You will set another one in the checkpoint.

Reading and setting environment variables. An environment variable is a named piece of text that a process carries with it. Examples are HOME or USERPROFILE, which hold your home folder, and PATH, which holds the program search list. The full set of these variables is called the process’s environment. The GNU Bash manual describes it as a list of name=value pairs that every program is given when it starts.

Environment variables exist so that settings can reach programs without being typed as arguments every time. A program reads the variables it cares about. ls, python and every program you write later can read them.

How programs get them. When the shell starts a program, that program becomes a child process of the shell. The shell is its parent process. The child receives a copy of the parent’s environment variables. The copy is made once, when the child starts. The child can change its own copy, but those changes never flow back to the parent. On the test machine, a child shell that set a variable and exported it left the parent seeing nothing.

The diagram below shows the direction of the copy.

flowchart TD
  S["Shell: PATH, HOME, exported variables"] -->|"copy at start"| C["Child program: its own copy"]
  C -.->|"changes never return"| S

The solid arrow shows the copy the child receives when it starts. The dotted arrow is there to make one point clear: changing a variable inside a child program never changes the shell that started it.

Shell variables and environment variables in bash and zsh. Bash and zsh keep two kinds of named values. A shell variable lives only inside the shell. An environment variable is also copied to every child program. NAME=value creates a shell variable. export NAME marks it for copying to children. export NAME=value does both in one step. PowerShell has no such split for this: a value written to $env:NAME is an environment variable immediately, and Microsoft’s documentation says environment variables are inherited by child processes.

This is how to read, set and remove a variable on each system:

Terminal window
echo $HOME
export TRACKER_MODE=test
echo $TRACKER_MODE
printenv TRACKER_MODE
unset TRACKER_MODE

Here is what each line does.

  • echo $HOME prints your home folder. The shell replaces $HOME with its value before echo runs, so echo receives the folder name as its argument. The $ in front of the name tells the shell to use the value; without it, echo HOME prints the word HOME.
  • export TRACKER_MODE=test creates the variable and marks it for children. There are no spaces around =. Bash and zsh require that.
  • printenv is a separate program. It prints the value of the named variable if the variable is in its environment. Because printenv is a child process, it proves the variable really was passed on. On the test machine it printed test, with exit status 0.
  • unset removes the variable.
  • In PowerShell, $env: in front of a name means “the environment variable with this name”. Assigning to it sets the variable, and Remove-Item Env: removes it. On Windows your home folder is in USERPROFILE. PowerShell also gives it to you as $HOME.

How long a change lasts. A variable you set this way exists only in this shell and in programs started from it. Close the window and the change is gone. A new window starts with the settings the system gives every new shell. Microsoft’s documentation says the same for PowerShell: changing an environment variable affects only the current session. Making a change permanent is done through configuration files and settings that later modules introduce when a tool needs them.

PATH is the variable that decides which programs run. To add a folder to PATH for this session, you keep the old value and add to it. You never replace it.

Terminal window
export PATH="$PATH:/opt/tools/bin"

In the bash line, the shell expands $PATH to the current list, adds a colon and the new folder, and stores the result. In PowerShell, += adds text to the end of the current value; the ; separates the new folder from the list. The folder names here are examples; neither folder needs to exist for you to understand the pattern.

Upper and lower case. On macOS and Linux, variable names are case-sensitive, so PATH and Path are two different variables. On Windows they are not case-sensitive. Microsoft’s environment variable documentation states both rules.

Common mistakes.

  • Spaces around = in bash or zsh. TRACKER_MODE = test is split into three words. The shell then looks for a program called TRACKER_MODE.
  • Forgetting export. The shell can see the variable, but child programs cannot. Nothing reports an error.
  • Forgetting the $ when reading. echo PATH prints the word PATH, not the list.
  • Replacing PATH instead of adding to it. After that, the shell cannot find programs such as ls. Closing the window undoes it, because the change never left that session.
  • Expecting the change to still be there in a new window. It is not, unless it was saved in a configuration file.

Where this connects. The next module checks PATH when python is “not found”. The module “Virtual environments and packages” works by putting a new folder at the front of PATH. The module “File and directory access” reads environment variables from Python with os.environ.

This section starts with the problems most people hit in the attempt. Each is explained using the section above. After that come nine deliberate failures. For each one, write down what you expect to happen before you run it, then run it and compare.

Typical problems in the attempt.

  • mkdir projects printed an error that the folder exists. You already had a projects folder. Nothing is wrong. The folder was left as it was, and you can move into it. mkdir -p would have stayed quiet (see “cd, ls and mkdir”).
  • A folder was created in an unexpected place. You ran mkdir from a different working directory than you thought. pwd shows where you are (see “The working directory”).
  • echo $? printed 0 after a command that clearly failed. Another command ran between the failure and the echo, often a second echo $?. The status describes only the command immediately before it (see “Exit status”).
  • In PowerShell, $? printed True or False, not a number. That is how PowerShell reports cmdlets. The number from a program file is in $LASTEXITCODE (see “Exit status”).
  • The command worked in one terminal but not in another. The two windows were running different shells, or the command came from instructions for a different shell (see “Terminal, shell and prompt”).
explaincli-terminal-vs-shell
On any operating system, a terminal window is open and you type whoami and press Enter. Explain which program reads the line, which program finds and starts whoami, and what the terminal application itself does.
predictcli-predict-word-split
In bash or zsh, in an empty folder, you run mkdir tax receipts 2026 with no quotation marks. Before running it: how many folders are created, what are their names, and what is the exit status?
applycli-apply-quote-name
Write one command, valid in bash, zsh and PowerShell, that creates a single folder named tax receipts in the working directory. Then say why your version produces one folder.
recallcli-recall-working-directory
Name the command that prints the working directory in bash and zsh, and say which PowerShell cmdlet the same word runs in PowerShell.
predictcli-predict-relative-path
On Linux the working directory is /home/sam/projects. You run cd ../study, and it succeeds. Before running pwd, write the absolute path it will print, and explain each step of how you got it.
comparecli-compare-absolute-relative
In a terminal, you can refer to a folder with an absolute path such as /home/sam/projects/expense-tracker or a relative path such as expense-tracker. State when each is the better choice, and the condition that decides it.
codecli-code-nested-folders
Starting in your home folder in bash or zsh, write three commands that create the folders work/receipts/2026 (none of them exist yet), move into 2026, and print the working directory.
diagnosecli-diagnose-nested-folder
You created expense-tracker earlier. Later, ls inside that folder shows another folder also named expense-tracker, and no error was ever printed. What caused it, and how do you prevent it?
explaincli-explain-path-search
In bash, you type whoami with no slash in the name. Explain the order in which the shell tries to find what to run, and what happens if nothing matches.
diagnosecli-diagnose-127
In bash you type pyhton --version and see bash: pyhton: command not found. Then echo $? prints 127. What does 127 tell you, and what are the two different causes you should check for a message like this?
predictcli-predict-status-overwrite
In bash on Linux you run ls missing.txt (the file does not exist), then echo $?, then echo $? again. Predict both printed numbers and explain the second one.
comparecli-compare-ps-status
In PowerShell, compare $? and $LASTEXITCODE: what does each hold, and after which kind of command should you read each one?
diagnosecli-diagnose-equals-spaces
In zsh you type TRACKER_MODE = test and see zsh: command not found: TRACKER_MODE. Explain the cause and give the correct line.
predictcli-predict-export
In bash you run TRACKER_MODE=test and then printenv TRACKER_MODE, without using export. Predict what printenv prints and its exit status, and explain why echo $TRACKER_MODE behaves differently.
refactorcli-refactor-path
A setup note says to run export PATH=/opt/tools/bin in bash so that the programs in that folder can be found. After running it, ls fails with command not found. Rewrite the line so it works, and say what was wrong.
designcli-design-path-choice
You are writing instructions for yourself that will be run months from now, in a new terminal window, to list the contents of ~/projects/expense-tracker/data. Choose between cd data followed by ls, and one command using a path that starts from the home folder. Defend the choice.
diagnosecli-diagnose-cd-missing
In bash, from your home folder, you run cd expense-tracker and get bash: cd: expense-tracker: No such file or directory, even though you know the folder exists at ~/projects/expense-tracker. Explain the cause and give two correct commands.
explaincli-explain-cd-builtin
In bash, type cd prints cd is a shell builtin, while type ls points to a file in /usr/bin. Explain why cd cannot be a separate program file, using what you know about how a child process gets its working directory.
diagnosecli-diagnose-mkdir-parent
On Linux, in an empty folder, mkdir notes/2026 prints mkdir: cannot create directory 'notes/2026': No such file or directory. Which folder is actually missing, and what is the one-command fix?
predictcli-predict-new-window
In PowerShell on Windows you run $env:CHECKPOINT_OWNER = 'Sam', and $env:CHECKPOINT_OWNER prints Sam. You then open a completely new PowerShell window and run $env:CHECKPOINT_OWNER there. Predict the output and explain it.

Stored in this browser only — it does not sync across devices, andlearner-profile.md remains the record.

Claim Source (anchored) Tier Read on
zsh is the default login and interactive shell from macOS Catalina; bash on Mojave and earlier; man zsh works in Terminal Apple Support, Use zsh as the default shell on your Mac 1 2026-09-21
Terminal is in the Utilities folder inside Applications, and Spotlight opens it Apple Community thread 3 2026-09-21
Windows Terminal is one application for shells such as Command Prompt, PowerShell and WSL Microsoft Command Line blog, Introducing Windows Terminal 2 2026-09-21
The shell breaks input into words and operators by its quoting rules, then expands, then executes GNU Bash manual, Shell Operation 1 2026-09-21
cd cannot be implemented outside the shell because it changes the shell itself Bash Reference Manual, older edition, Introduction 1 2026-09-21
Absolute paths start with / and are looked up from the root; relative paths start from the working directory; the working directory is inherited from the parent and changed by chdir Linux man-pages, path_resolution(7), Step 1 1 2026-09-21
Windows absolute and relative paths; C:Projects versus C:\Projects; separators are normalised Microsoft .NET docs, File path formats on Windows systems 1 2026-09-21
cd, ls and pwd are Windows aliases for Set-Location, Get-ChildItem and Get-Location; mkdir corresponds to New-Item Microsoft PowerShell docs, Using aliases, compatibility aliases in Windows 1 2026-09-21
HOME is the default for cd and is used by tilde expansion; PATH is a colon-separated list of folders searched for commands GNU Bash manual, Bourne Shell Variables 1 2026-09-21
Function, then builtin, then PATH search; hash table; not found gives 127 GNU Bash manual, Command Search and Execution 1 2026-09-21
0 is success, non-zero is failure; 127 not found; 126 not executable; $? holds the last status GNU Bash manual, Exit Status 1 2026-09-21
Most commands use 0 for success and a non-zero value for failure; ls is one of the exceptions GNU Coreutils manual, Exit status 1 2026-09-21
GNU ls: -a or --all shows entries starting with .; exit 1 for minor problems, 2 for serious trouble ls(1), Exit status section 1 2026-09-21
$? is True or False; for native programs it is True when $LASTEXITCODE is 0; $LASTEXITCODE holds the last native program’s exit code about_Automatic_Variables, PowerShell 7.6 1 2026-09-21
Windows PowerShell 5.1 error layout, with At line and CategoryInfo lines about_Automatic_Variables source for 5.1 1 2026-09-21
Older PowerShell wording: “operable program” Verbatim error in a PowerShell 6.0 issue report 0 2026-09-21
The environment is a list of name=value pairs; executed commands inherit it; export adds variables to it GNU Bash manual, Environment, text read from the mirror at w3.pppl.gov/info/bash/Environment.html because gnu.org returned HTTP 429 1 2026-09-21
PowerShell environment variables are inherited by child processes, affect only the current session, and are case-sensitive on macOS and Linux only; ; on Windows, : elsewhere; Remove-Item Env: about_Environment_Variables 1 2026-09-21
macOS printenv prints a named variable and exits greater than 0 on error printenv(1), macOS 15.2 man page mirror 1 2026-09-21
whoami exists on Windows and prints the domain and user name Windows commands, whoami 1 2026-09-21
Every output block on this page: error strings, exit statuses, type results, the printenv behaviour, New-Item creating parent folders, and the space and nested-folder silent breaks Run by the author in bash 5.2.21, zsh 5.9 and PowerShell 7.6.6 with GNU coreutils 9.4, Ubuntu 24.04 0 2026-09-21

Written against bash 5.2.21, zsh 5.9, PowerShell 7.6.6 and GNU coreutils 9.4, verified 2026-09-21. The macOS ls, Windows PowerShell 5.1 output and Windows mkdir behaviour were not run and are described only where a source supports the description.

Half-life layer 1. Working directories, absolute and relative paths, the PATH search, exit statuses and inherited environments come from the Unix and POSIX shell design that bash and zsh still follow. PowerShell uses the same ideas under different names. These rules change over decades, not years, so they are worth learning once, properly. Individual command spellings and error wording change faster, and the page links to the reference for those.