---
title: Command line basics
description: Understand how a Linux command is built, learn the essential terminal commands, what their names mean, and get a cheat sheet to keep at hand.
---


:ellipsis{left=0px width=40rem top=10rem blur=140px zIndex=60}

A server has no desktop, no icons and no mouse. Everything happens in a terminal, and that black window with a blinking cursor is the single thing that puts people off self-hosting. It shouldn't: the terminal is just a conversation. You type one line, the machine does exactly that and answers. Nothing more magic than a search bar, except it does far more and never hides an option behind three menus.

The good news is that you don't need to know a hundred commands. Ten of them cover almost everything you'll do on a home server, and they all follow the same pattern. Learn the pattern first, and every command you meet later becomes readable, even the ones you've never seen.

## How a command is built

Every command line, without exception, is the same sentence: **what to run**, **how to run it**, **what to run it on**.

```text [Anatomy of a command]
sudo apt install -y nano
│    │   │       │  └─ argument: what the command works on
│    │   │       └──── option: changes how it behaves
│    │   └──────────── subcommand: what the program should do
│    └──────────────── the program you're running
└───────────────────── run it with administrator rights
```

Read out loud, that line says "as an administrator, ask the package manager to install the nano package, and don't ask me to confirm". Spaces are what separate the pieces, which is why a folder named `My Backups` has to be quoted (`cd "My Backups"`) or the shell reads it as two different things.

### Options, short and long

Options change how a command behaves. They come in two flavours, and most commands accept both:

- **Short**, a single dash and a single letter: `ls -a`. They can be stacked, so `ls -l -a -h` is usually written `ls -lah`.
- **Long**, two dashes and a whole word: `ls --all`. Longer to type, but you can still tell what it does six months later, which is why they're the better choice in a script.

Some options expect a value right after them: `ssh-keygen -t ed25519` (`-t` for type), `rsync --exclude @eaDir`. And case matters, always. In `ls`, `-r` reverses the sort order while `-R` walks into subfolders. Two different things, one letter apart.

### Arguments and paths

The argument is the target: a file, a folder, a package name, an address. Many commands accept several at once, separated by spaces, which is what makes the terminal fast: `rm file1.txt file2.txt file3.txt` deletes three files in one go.

When the target is a place on the disk, you write it as a path, and there are a few shortcuts worth knowing:

| Path | Means |
| --- | --- |
| `/` | the root of the whole system, everything lives under it |
| `~` | your own home folder, `/home/username` |
| `.` | the folder you're currently in |
| `..` | the folder just above |
| `/var/log` | an **absolute** path, same result from anywhere |
| `logs/today` | a **relative** path, understood from where you currently stand |

Which folder holds what is a subject of its own, covered in [folders and partitions](/general/linux/filesystem).

The prompt itself tells you where you are: in `username@serveex:~/docker$`, you're logged in as `username` on the machine named `serveex`, inside the `docker` folder of your home. That final `$` means a normal user. If it ever shows `#`, you're root and every typo counts double.

### Getting help

Two habits make you independent from tutorials. `command --help` prints a quick summary of every option, and `man command` opens the full manual (`man` for *manual*), which you leave by pressing :kbd{value="Q"}.

::tip{icon=""}
✨ __Tip:__ three keyboard habits that change everything: :kbd{value="Tab"} completes the file or folder name you started typing, so you almost never type a full path; the :kbd{value="Up"} arrow brings back your previous commands, which saves retyping a long line for one character; and :kbd{value="Ctrl"} + :kbd{value="C"} stops whatever is currently running.
::

### Chaining commands

Once the pattern clicks, commands can be plugged into each other:

- `&&` runs the next one only if the previous one succeeded: `sudo apt update && sudo apt full-upgrade`
- `|`, the pipe, feeds the output of one command into another: `ls -l | grep backup` lists the folder, then keeps only the lines containing "backup"
- `>` writes the output into a file instead of the screen, and `>>` adds to the end of that file: `df -h > disk-report.txt`

## The commands you'll actually use

Most command names are abbreviations of an English phrase. Once you know what they stand for, they stop looking like keyboard noise.

### `pwd`, print working directory

Tells you where you are. It changes nothing, it just answers the question.

```bash [Terminal]
pwd
```

```console [Output]
/home/username/docker
```

### `ls`, list

Lists what's in the current folder. On its own it prints bare names, so it's almost always used with options: `-l` for the long format with sizes, dates and permissions, `-a` to also show hidden files (the ones starting with a dot), `-h` for sizes in K/M/G instead of raw bytes.

```bash [Terminal]
ls -lah
```

```console [Output]
total 20K
drwxr-xr-x  4 username username 4.0K Sep  5 10:12 .
drwxr-xr-x 18 username username 4.0K Sep  4 21:03 ..
-rw-r--r--  1 username username  512 Sep  5 10:12 .env
-rw-r--r--  1 username username 1.2K Sep  5 09:58 compose.yaml
drwxr-xr-x  3 username username 4.0K Sep  2 18:44 immich
```

The first column is the permissions, `d` at the very start meaning it's a folder. Then the owner, the size, the date of the last change, and the name.

### `cd`, change directory

Moves you around. With a path it goes there, with `..` it goes up one level, and with nothing at all it takes you back home.

```console [Terminal]
username@serveex:~/docker$ cd /var/log
username@serveex:/var/log$ cd ..
username@serveex:/$ cd
username@serveex:~$
```

Notice the prompt following you around: it always shows where you currently stand, so you rarely need `pwd` in practice.

### `mkdir`, make directory

Creates a folder. Several at once if you list them, and `-p` creates the whole chain of parents in one shot, which is the version you'll actually use.

```bash [Terminal]
mkdir backups
mkdir -p docker/immich/config
```

```console [Output]

```

Nothing. That's not a bug, it's the rule: most commands say nothing when they succeed and only speak up when something goes wrong. Silence is good news, and `ls` confirms the folder is there.

### `cp` and `mv`, copy and move

`cp` copies, `mv` moves. Same shape both times: first the source, then the destination. Copying a folder needs `-r`, for *recursive*, since a folder means everything inside it too. `mv` doubles as the rename command, because renaming a file is just moving it to a new name.

```bash [Terminal]
cp compose.yaml compose.yaml.bak
cp -r config/ config-backup/
mv old-name.txt new-name.txt
ls
```

```console [Output]
compose.yaml  compose.yaml.bak  config  config-backup  new-name.txt
```

Three silent commands, and `ls` showing the result: the copy sits next to the original, the folder was duplicated, and `old-name.txt` is gone because moving it to another name is exactly what renaming means.

### `rm`, remove

Deletes. There is no recycle bin, no undo, no confirmation dialog. `-r` deletes a folder and its contents, `-f` forces without asking.

::warning{to="/nonsense/bash/rm-confirmation"}
`rm -rf` is the command that wipes homelabs. It doesn't check, doesn't warn, and doesn't stop. Read the path twice before pressing :kbd{value="Enter"}, especially when the line starts with `sudo` and contains a `/` or a `*`. You can also prevent this by wrapping `sudo` in a small Bash function that asks **"are you sure?"** before it lets an `rm` through, covered in **rm confirmation guard**.
::

### `cat` and `nano`, read and edit

`cat` (short for *concatenate*) dumps a whole file to the screen, perfect for a short config. For anything longer, `less` scrolls through it (named as a joke on `more`, the older pager it replaced), and you quit it with :kbd{value="Q"}.

To actually change a file, `nano` opens a simple editor: arrows to move, :kbd{value="Ctrl"} + :kbd{value="O"} to save, :kbd{value="Ctrl"} + :kbd{value="X"} to leave.

```bash [Terminal]
cat .env
```

```properties [Output]
PUID=1000
PGID=1000
TZ=Europe/Paris
```

### `grep`, search inside files

`grep` stands for *global regular expression print*, which is a mouthful for "find me this text". You give it what to look for and where, and it prints every matching line. `-r` searches a whole folder, `-i` ignores upper and lower case, `-n` shows line numbers.

```bash [Terminal]
grep -rin "password" /home/username/docker
```

```console [Output]
/home/username/docker/immich/.env:6:DB_PASSWORD=changeme
/home/username/docker/vaultwarden/compose.yaml:14:  ADMIN_PASSWORD=hunter2
```

Each line is the file, then the line number inside it, then the matching line itself. Very handy for the day you can't remember which stack holds a setting.

### `sudo`, run as administrator

*Substitute user do*. A normal user can't touch the system's files, which is exactly what protects you from wrecking the machine by accident. Prefixing a command with `sudo` runs that single command with administrator rights, and asks for your password the first time.

```bash [Terminal]
nano /etc/ssh/sshd_config
```

```console [Output]
Error writing /etc/ssh/sshd_config: Permission denied
```

```bash [Terminal]
sudo nano /etc/ssh/sshd_config
```

```console [Output]
[sudo] password for username:
```

::note
If a command answers `Permission denied`, that's usually the whole problem: it needed `sudo`. Resist the reflex of putting `sudo` on everything though, a file created as root will keep annoying you afterwards because your normal user no longer owns it.
::

## Cheat sheet

The ones worth keeping at hand, and where their names come from.

| Command | Short for | What it does |
| --- | --- | --- |
| `pwd` | print working directory | Shows where you are |
| `ls` | list | Lists files and folders |
| `cd` | change directory | Moves you somewhere else |
| `mkdir` | make directory | Creates a folder |
| `touch` | plain English | Creates an empty file, or refreshes its date |
| `cp` | copy | Copies a file or folder |
| `mv` | move | Moves or renames |
| `rm` | remove | Deletes, permanently |
| `cat` | concatenate | Prints a file to the screen |
| `less` | a pun on `more` | Scrolls through a long file |
| `nano` | the editor replacing Pico | Edits a file |
| `grep` | global regular expression print | Searches for text |
| `find` | plain English | Searches for files by name, size or date |
| `man` | manual | Opens a command's full documentation |
| `df` | disk free | Shows free space per partition |
| `lsblk` | list block devices | Draws the tree of disks and partitions |
| `du` | disk usage | Shows what a folder weighs |
| `ps` | process status | Lists running processes |
| `htop` | Hisham's `top` | Live view of CPU, RAM and processes |
| `kill` | plain English | Stops a process by its number |
| `chmod` | change mode | Changes a file's permissions |
| `chown` | change owner | Changes who owns a file |
| `sudo` | substitute user do | Runs one command as administrator |
| `apt` | advanced package tool | Installs, updates and removes packages |
| `systemctl` | control systemd | Starts, stops and enables services |
| `ssh` | secure shell | Opens a session on a remote machine |
| `scp` | secure copy | Copies files over SSH |
| `tar` | tape archive | Packs and unpacks archives |
| `wget` | web get | Downloads a file from a URL |
| `curl` | client URL | Sends a request to a URL |
| `history` | plain English | Lists the commands you typed before |

::tip{icon=""}
✨ __Tip:__ nobody memorises this. You'll look up the same three options for weeks, then one day realise you're typing them without thinking. Until then, `--help` and this table are perfectly legitimate.
::
