LPIC-1 CLI Fundamentals: vi, FHS, Filesystem Integrity, and the Shell Environment
- Edit a file in vi/vim by switching deliberately between command mode and insert mode
- Predict where a Linux system keeps config, data, and programs using the Filesystem Hierarchy Standard
- Find a command or file with the right tool: which/type, find, and locate
- Diagnose disk space and inode exhaustion with df, size directories with du, and run fsck safely
- Set environment variables and aliases, and make them survive a new login session
vi is modal — and that's the whole secret
Build the mental model that explains why vi feels broken until it suddenly doesn't.
Sooner or later a Linux box hands you
viand nothing else. A failed boot drops you into a recovery shell, a minimal container ships withoutnano, and the LPIC-1 exam assumes you can edit/etc/fstabwith it. So you learnvi(orvim, the improved version that's almost always whatviactually points to) not because it's pretty, but because it's always there.The one idea that unlocks
viis that it is modal. Most editors are always ready for you to type text.viis not. At any moment it is in one of two modes, and the same keystroke does different things depending on the mode you're in.- Command mode (where you start): keys are commands, not text. Pressing
ddeletes,iswitches modes — nothing you type lands as letters on the page. - Insert mode: now keys are text. Typing
hellowriteshello.
The analogy: think of
vias a car with a clutch. An automatic (likenano) just goes when you press the pedal. With a manual, you have to be in gear to move. Pressingiis letting out the clutch into "drive" (insert mode); pressingEscis shifting back to neutral (command mode) where the controls steer instead of accelerate. Stalling at first is normal — the muscle memory of "Esc, then act" becomes automatic fast.vim /etc/hostname- i
- INSERT before the cursor — the most common way in.
- a
- APPEND after the cursor — insert one character to the right.
- o
- OPEN a new line below the cursor and start inserting there.
- Esc
- Leave insert mode, returning to command mode. Your reflex anchor.
Opens the file in command mode. Nothing you type as letters appears yet — that is expected, not a bug. To put text in, you must first enter insert mode with one of the keys below. Opens the file in vim (no stdout — it takes over the terminal). :q quits, :wq saves and quits, :q! discards. Saving and quitting are command-mode actions that begin with
:(which opens a one-line command prompt at the bottom). The four you must know cold::wq— write (save) then quit.:q!— quit and throw away changes (the!forces it).:w— save but stay in the file.:q— quit, but only if there are no unsaved changes.
And three command-mode editing moves that the exam loves:
dddeletes (cuts) the current line,yyyanks (copies) it, andpputs (pastes) what you cut or copied below the cursor.Example — not runnable on your lab box
:wq"/etc/hostname" 1L, 9B written
Typed in command mode (after pressing Esc). The status line confirms the write — here 1 line, 9 bytes. If you only wanted to bail out without saving,:q!would have discarded every change instead.Check yourself
You opened a file in vim, typed some text that appeared as letters, and now want to save and exit. What two things must you do, in order?
- Command mode (where you start): keys are commands, not text. Pressing
The FHS: a map of where everything lives
Predict the right directory for config, variable data, and programs without guessing.
Move between a Debian box, a Red Hat box, and an Alpine container and you'll find the same files in the same places. That consistency isn't luck — it's the Filesystem Hierarchy Standard (FHS), an agreement on what each top-level directory is for. Knowing the FHS means you can walk onto an unfamiliar system and know where to look before you search.
The analogy: the FHS is a well-organized kitchen. The pantry (
/etc) holds the recipes and settings, the fridge (/var) holds the perishable, ever-changing stuff (logs, mail, spools), and the cabinets (/usr) hold the appliances and tools you actually cook with. You don't rummage randomly — you know which cupboard before you open it.The directories LPIC-1 expects you to place correctly:
- `/etc` — system-wide configuration, always plain text.
/etc/fstab,/etc/passwd,/etc/ssh/sshd_config. Never user data, never binaries. - `/var` — variable data that grows and changes while the system runs:
/var/log(logs),/var/spool(print/mail queues),/var/cache. - `/usr` — the bulk of installed programs and their support files:
/usr/bin,/usr/lib,/usr/share. Think of it as read-only application code. - `/bin` vs `/sbin` — essential commands.
/binholds tools for everyone (ls,cp,cat);/sbinholds system-administration tools that normally need root (fdisk,fsck,ip). Thesis for superuser/system.
Check yourself
A service is misbehaving. Its configuration file lives under which top-level directory, and its log file lives under which?
- `/etc` — system-wide configuration, always plain text.
Finding files: which/type vs find vs locate
Pick the right search tool for the question you're actually asking.
"Where is it?" has three different answers depending on what you're hunting, and using the wrong tool is slow or misleading. The trick is to match the tool to the question.
- "Which program runs when I type this command?" →
whichortype(they search yourPATH). - "Where is any file matching this, right now, anywhere?" →
find(it walks the live filesystem). - "Where's that file — quickly, from an index?" →
locate(it queries a prebuilt database).
type -a lsls is aliased to `ls --color=auto' ls is /usr/bin/ls
- -a
- Show ALL matches — every alias, function, builtin, and PATH binary, not just the first.
typeanswers "what happens when I runls?" — and it sees thingswhichcan't. Here it revealslsis both an alias and a binary at/usr/bin/ls; the alias wins.whichwould only have reported the binary path, hiding the alias entirely.find /etc -name '*.conf' -type f/etc/resolv.conf /etc/ld.so.conf /etc/sysctl.conf /etc/host.conf
- /etc
- Where to start the walk (the starting directory).
- -name '*.conf'
- Match by filename pattern. Quote the glob so the shell doesn't expand it first.
- -type f
- Match regular files only, excluding directories and symlinks.
findwalks the directory tree live, so its results are always current — it cannot be stale. The cost is speed: it reads every directory under/etcon the spot. Here it finds real.conffiles that exist this instant.locate sshd_config/etc/ssh/sshd_config /usr/share/man/man5/sshd_config.5.gz
- locate
- Query the filename index built by updatedb — fast, but only as current as the last index.
locateanswers instantly because it isn't searching the disk — it's reading a precomputed database (mlocate/plocate). That speed is also its weakness: the database is only as fresh as the last time it was rebuilt.Check yourself
You just created /opt/newapp/config.yml ten seconds ago and need to confirm its exact path. Would you trust
locate config.ymlor usefind? Why in one word?- "Which program runs when I type this command?" →
Filesystem integrity: space, inodes, and fsck
Diagnose a 'disk full' that isn't, size directories, and run fsck without destroying data.
When a system says it's out of space, there are actually two ways that can be true, and confusing them wastes hours. A filesystem tracks two separate resources:
- Blocks — the actual bytes of file content. Run out and you can't write more data.
- Inodes — the bookkeeping entries, one per file or directory, that store a file's metadata. Run out and you can't create a new file even though gigabytes are free, because there's no inode left to describe it.
dfreports both — you just have to ask the right way.df -hFilesystem Size Used Avail Use% Mounted on /dev/vda1 20G 18G 1.2G 94% / tmpfs 2.0G 0 2.0G 0% /dev/shm /dev/vdb1 50G 12G 38G 24% /data
- -h
- Human-readable sizes (G/M/K) instead of raw 1K blocks.
Free space by filesystem. The /root is at 94% used with only 1.2G left — the kind of thing that breaks logging and updates. TheMounted oncolumn matters for the next command: each line is a separate mounted filesystem.df -iFilesystem Inodes IUsed IFree IUse% Mounted on /dev/vda1 1310720 1310702 18 100% / tmpfs 512000 1 511999 1% /dev/shm /dev/vdb1 3276800 41203 3235597 2% /data
- -i
- Report inode usage (count of files) instead of block/byte usage.
The same disks, but counting inodes instead of bytes. Root /is at 100% IUse — every inode is taken. This is the classic trap:df -hmight show free space, yet you still get "No space left on device" because you've run out of inodes. The usual cause is a directory stuffed with millions of tiny files (a runaway cache or session-file dump).Once you know a filesystem is tight, find the culprit directory.
du("disk usage") sums up sizes from the bottom up — the complement todf's top-down view.du -sh /var/log4.7G /var/log
- -s
- Summary only — one total for the whole directory, not a line per file.
- -h
- Human-readable sizes, matching df's -h.
Totals one directory and its contents — here /var/logalone is eating 4.7G, a strong lead for the 94%-full root above. Drop the-sandduprints every subdirectory's size, useful for drilling down to the exact offender.To rank a directory's children instead, give
du -sa glob and sort the numbers:du -s /var/* | sort -rn | headputs the biggest first (leave out-hhere sosort -ncompares plain numbers). And trustduoverls -lwhen hunting space:ls -lshows a file's apparent size, the length it claims, whileducounts the blocks actually allocated. A sparse file, such as a VM image, can claim far more than it uses:truncate -s 1G sparse.img && ls -lh sparse.img | awk '{print $5}' && du -h sparse.img1.0G 0 sparse.img
- truncate -s 1G
- Set the file's length to 1 GiB without writing any data: a sparse file.
- --apparent-size
- (du) report the claimed length like ls -l does, instead of the allocated blocks.
ls -lreports 1.0G;dureports 0, because no block was ever written. Rank directories by anls -ltotal and a directory holding one big sparse image would look like the culprit while using nothing.dumeasures what the disk is really spending. Delete the demo withrm sparse.img.Sometimes the problem isn't space at all — it's corruption, usually after a crash or unclean power-off.
fsck(filesystem check) repairs a damaged filesystem's structure. But it carries one absolute rule.sudo fsck /dev/vdb1fsck from util-linux 2.38.1 e2fsck 1.47.0 /dev/vdb1 is mounted. e2fsck: Cannot continue, aborting.
- /dev/vdb1
- The block DEVICE to check — note you pass the device, not the mount point.
fsckrefused on purpose. You must check an unmounted filesystem. The fix is to unmount first (sudo umount /data), runfsck /dev/vdb1, then remount — or, for the root filesystem that can't be unmounted while running, schedule the check for the next boot.Check yourself
A user reports 'No space left on device', but
df -hshows the root filesystem is only 30% used. What single command checks the most likely real cause?The shell environment: export, alias, and making them stick
Customize the shell and ensure your changes survive logging out and back in.
Your shell has a memory: environment variables (like
PATH,EDITOR,LANG) and aliases (shorthands you define). Two questions decide whether a setting behaves the way you expect:1. Will child processes (scripts and programs you launch) see this variable?
2. Will it still be set in a brand-new shell after you log out and back in?
Getting the first wrong is the
exporttrap; getting the second wrong is the persistence trap.EDITOR=vim; bash -c 'echo "child sees: $EDITOR"'child sees:
EDITOR=vimsets a shell variable that lives only in this shell. The child shell (bash -c ...) prints an empty value — it never inherited it. A variable withoutexportis private to the shell that created it.export EDITOR=vim; bash -c 'echo "child sees: $EDITOR"'child sees: vim
- export
- Mark a variable for inheritance by child processes, turning a shell variable into an environment variable.
Adding exportpromotes the variable to an environment variable, which child processes inherit. Now the subshell seesvim. This is why programs that read$EDITOR(likecrontab -e) only respect it when it's exported.alias ll='ls -alF'Creates a shortcut: typing llnow expands tols -alF. Success is silent. But there's a catch the next block addresses — this alias vanishes the moment you close the terminal.Both the
exportand thealiasabove die when the shell exits, because they live only in memory. To make them permanent, write them into a startup file that the shell reads every time it starts:- `~/.bashrc` — runs for your interactive shells. The right home for personal aliases and variables.
- *`/etc/profile.d/.sh
** — any.shfile dropped here runs for **every user's** login shell. The right home for *system-wide* settings, and cleaner than editing/etc/profile` directly.
echo "alias ll='ls -alF'" >> ~/.bashrc && source ~/.bashrc- >>
- Append to the file. Using a single
>would OVERWRITE .bashrc — a destructive mistake. - source
- Execute the file's lines in the current shell so changes apply immediately, without opening a new terminal.
>>appends the alias line to~/.bashrc(using>>, not>, so you add to the file instead of erasing it).source ~/.bashrcre-reads the file into the current shell so the change takes effect now — otherwise you'd only see it in the next new terminal.Check yourself
You add an alias at the prompt and it works perfectly, but it's gone after you close the terminal. Which file would you edit so your personal alias loads in every future shell?
Try it in your live terminal
Run these safe, read-only commands on the real sandbox to your right.
You have a real shell. Every command below is read-only — it inspects the system without changing anything — so run them freely and read the output against what each step taught. This is the inspection routine you'd reach for on any unfamiliar box.
df -hFree space per filesystem. Find the one mounted on /and note itsUse%— is anything dangerously full?df -iThe same filesystems by inode usage. Compare IUse%against theUse%from above — they can differ wildly, and that difference is the whole point.du -sh /etc 2>/dev/nullTotal size of the entire config directory. 2>/dev/nulldiscards any 'permission denied' noise so you get one clean total.type -a lsReveals everything lsresolves to — likely an alias plus the/usr/bin/lsbinary. Confirms what actually runs when you type the command.aliasLists every alias currently defined in your shell. These are the shortcuts loaded from your startup files — your environment, made visible.
- vi is modal: you're always in command mode or insert mode, and the same key means different things in each. When confused, press
Escfirst, then act. - The FHS makes file locations predictable:
/etcfor config,/varfor changing data,/usrfor programs,/binfor everyone vs/sbinfor admins. - Match the search tool to the question:
which/typefor PATH commands,findfor live (always-current) filesystem walks,locatefor fast indexed lookups that can be stale. - Disk trouble has two forms:
df -h(bytes) anddf -i(inodes) — you can exhaust either independently.du -shfinds the heavy directory, andfsckonly runs on an unmounted filesystem. - Settings typed at the prompt are temporary:
exportmakes a variable visible to child processes, and writing it to~/.bashrcor/etc/profile.d/*.shmakes it survive new sessions.
| Command | Answers / does | Watch for |
|---|---|---|
i / a / o | Enter insert mode in vi | You're now typing TEXT, not commands |
Esc | Return to command mode | Your universal 'I'm stuck' escape |
:wq / :q! | Save+quit / quit discarding changes | Must be in command mode (Esc first) |
type -a cmd | What a command resolves to | Sees aliases that which misses |
find <dir> -name | Live filesystem search | Always current; slower |
locate <name> | Indexed filename search | Fast but STALE — run updatedb |
df -h | Free space (bytes) | High Use% breaks writes |
df -i | Free inodes (file count) | Can hit 100% with space free |
du -sh <dir> | Total size of a directory | Finds the space hog |
fsck <device> | Repair filesystem structure | UNMOUNT first or risk corruption |
export VAR=val | Make variable inheritable | Without it, children see nothing |
>> ~/.bashrc | Persist alias/var per user | Use >> not >; /etc/profile.d for all users |
