Chapter 5

Where software comes from

On Windows you download a program and double-click it. Linux does something different, and better. This chapter explains how software arrives, where it lands, and how the shell then finds it.

A package is a program plus its paperwork

Software on Ubuntu comes in packages. A package is one file that holds:

That third item matters most. Almost no program stands alone. It needs libraries, which are lumps of code shared between many programs. The package says what it needs, so the system can fetch those too.

This is why one small install pulls in others

In step 3 you asked for clang, and a long list of other names went past. Those were its dependencies — the pieces clang cannot work without. You did not have to know their names. The package did.

Packages live in a shop

Ubuntu keeps its packages on servers called repositories. Think of a repository as a shop with a catalogue: tens of thousands of programs, all built and tested to work together.

Your machine holds a copy of the catalogue, not the shop. This explains a command you have run without thinking:

CommandWhat it does
apt-get update Fetch a fresh catalogue. Downloads no programs at all.
apt-get install nano Look in the catalogue, fetch nano and whatever it needs, and put it in place.

update before install is a habit worth keeping. An old catalogue points at versions the shop has already replaced, and the install fails for a reason that looks like nonsense.

Why you have to type sudo

A program installed this way goes into a system folder such as /usr/bin. Those folders do not belong to you. They belong to an account called root, which is allowed to change anything.

sudo means: run this one command as root. It asks for your password to check that it is really you sitting there.

This is a safety feature, not an obstacle. Because your normal account cannot write to system folders, a mistake you make — or a bad program you run by accident — can only damage your own files, not the whole machine.

A useful test

If a command needs sudo, it is about to change the machine for everybody. If it does not, it only touches your own files. That is a good moment to read the line once more before pressing Enter.

How Ubuntu knows a package is genuine

Packages arrive over the internet. Something must check that they came from who they claim, and were not changed on the way.

Each repository signs its catalogue with a key. Your machine keeps the matching key and checks every download against it. If the check fails, the install stops.

This is what step 5 is doing when it adds a key before installing Java. The JDK comes from Adoptium, not from Ubuntu, so your machine has to be told that Adoptium can be trusted — once, on purpose, in the open.

Where the program ends up

Programs from packages nearly always land in one place:

$ which python3
/usr/bin/python3

As chapter 1 showed, that is simply a file. The package put it there, and sudo is why it was allowed to.

PATH is the shell's list of places to look

You type python3, not /usr/bin/python3. So how does the shell find it?

It keeps a list of folders to search, in order. The list is called PATH. When you type a name, the shell walks the list, folder by folder, and runs the first match it finds.

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/games

The colons separate the folders. Read it as a list:

Now ./hello makes complete sense

The folder you are standing in is not on that list. So typing hello makes the shell search four folders, none of which hold your new program, and report command not found.

./hello is not a name to search for. It is a path. It tells the shell to skip the list and look exactly here.

A folder of your own

Not every program needs sudo. Some are installed just for you, into a folder inside your home:

~/.local/bin

That is where uv goes in step 8, and where bat and fd get their proper names in step 6. No sudo needed, because it is your own folder.

But it only works if that folder is on your PATH. On a fresh Ubuntu it often is not, which is why several of the setup scripts add it.

.bashrc runs every time you open a terminal

PATH is not carved in stone. It is set up fresh for every shell you start. The shell builds it by reading a settings file in your home folder called .bashrc — hidden, because its name starts with a dot.

When a script adds a line like this to .bashrc:

export PATH="$HOME/.local/bin:$PATH"

it is saying: from now on, look in my own folder first, then everywhere you were already looking.

The most common surprise in the whole setup

A script edits .bashrc and finishes. You type the new command and get command not found. Nothing is broken.

Your shell read .bashrc when it started, minutes ago. It has not read it again. The new line is in the file but not in this shell.

Two ways to fix it. Read the file into the shell you are in:

$ source ~/.bashrc

Or simply close the terminal and open a new one, which reads it anyway.

What "command not found" really means

It almost never means the program is missing. It means one of these:

Work through them in that order and you will find it every time.

Not everything comes from apt

Two steps deliberately go outside the shop, and it is worth knowing why.

ToolWhere it comes fromWhy
uv its own installer script It is new and moves quickly. Ubuntu's catalogue would always be behind.
gradle downloaded from Gradle Ubuntu's version is usually several years old.

The trade is simple. From the shop you get checking, dependency handling and tidy removal, but sometimes an older version. From outside you get today's version, and you take responsibility for trusting the source yourself.

Try it

See your search list:

$ echo $PATH

Find which folder a program came from:

$ which bash

Ask the catalogue about a package, without installing anything:

$ apt-cache policy bash

Count how many packages are already installed on your machine:

$ dpkg --get-selections | wc -l

Look at the lines the setup scripts added to your settings file:

$ grep system_setup ~/.bashrc

You can now explain