12 KiB
title, description
| title | description |
|---|---|
| Command line basics | 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.
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, sols -l -a -his usually writtenls -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.
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 backuplists 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.
pwd
/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.
ls -lah
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.
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.
mkdir backups
mkdir -p docker/immich/config
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.
cp compose.yaml compose.yaml.bak
cp -r config/ config-backup/
mv old-name.txt new-name.txt
ls
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.
cat .env
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.
grep -rin "password" /home/username/docker
/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.
nano /etc/ssh/sshd_config
Error writing /etc/ssh/sshd_config: Permission denied
sudo nano /etc/ssh/sshd_config
[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.
::