Signal9Signal9 — home
online
all lessons

LPIC-1 CLI Fundamentals: vi, FHS, Filesystem Integrity, and the Shell Environment

Beginner35 min6 steps
What you'll learn
  • 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
  1. 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 vi and nothing else. A failed boot drops you into a recovery shell, a minimal container ships without nano, and the LPIC-1 exam assumes you can edit /etc/fstab with it. So you learn vi (or vim, the improved version that's almost always what vi actually points to) not because it's pretty, but because it's always there.

    The one idea that unlocks vi is that it is modal. Most editors are always ready for you to type text. vi is 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 d deletes, i switches modes — nothing you type lands as letters on the page.
    • Insert mode: now keys are text. Typing hello writes hello.

    The analogy: think of vi as a car with a clutch. An automatic (like nano) just goes when you press the pedal. With a manual, you have to be in gear to move. Pressing i is letting out the clutch into "drive" (insert mode); pressing Esc is 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: dd deletes (cuts) the current line, yy yanks (copies) it, and p puts (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?

  2. 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. /bin holds tools for everyone (ls, cp, cat); /sbin holds system-administration tools that normally need root (fdisk, fsck, ip). The s is 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?

  3. 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?" → which or type (they search your PATH).
    • "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 ls
    ls 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.
    type answers "what happens when I run ls?" — and it sees things which can't. Here it reveals ls is both an alias and a binary at /usr/bin/ls; the alias wins. which would 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.
    find walks the directory tree live, so its results are always current — it cannot be stale. The cost is speed: it reads every directory under /etc on the spot. Here it finds real .conf files 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.
    locate answers 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.yml or use find? Why in one word?

  4. 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.

    df reports both — you just have to ask the right way.

    df -h
    Filesystem      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. The Mounted on column matters for the next command: each line is a separate mounted filesystem.
    df -i
    Filesystem      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 -h might 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 to df's top-down view.

    du -sh /var/log
    4.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/log alone is eating 4.7G, a strong lead for the 94%-full root above. Drop the -s and du prints every subdirectory's size, useful for drilling down to the exact offender.

    To rank a directory's children instead, give du -s a glob and sort the numbers: du -s /var/* | sort -rn | head puts the biggest first (leave out -h here so sort -n compares plain numbers). And trust du over ls -l when hunting space: ls -l shows a file's apparent size, the length it claims, while du counts 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.img
    1.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 -l reports 1.0G; du reports 0, because no block was ever written. Rank directories by an ls -l total and a directory holding one big sparse image would look like the culprit while using nothing. du measures what the disk is really spending. Delete the demo with rm 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/vdb1
    fsck 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.
    fsck refused on purpose. You must check an unmounted filesystem. The fix is to unmount first (sudo umount /data), run fsck /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 -h shows the root filesystem is only 30% used. What single command checks the most likely real cause?

  5. 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 export trap; getting the second wrong is the persistence trap.

    EDITOR=vim; bash -c 'echo "child sees: $EDITOR"'
    child sees:
    EDITOR=vim sets a shell variable that lives only in this shell. The child shell (bash -c ...) prints an empty value — it never inherited it. A variable without export is 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 export promotes the variable to an environment variable, which child processes inherit. Now the subshell sees vim. This is why programs that read $EDITOR (like crontab -e) only respect it when it's exported.
    alias ll='ls -alF'
    Creates a shortcut: typing ll now expands to ls -alF. Success is silent. But there's a catch the next block addresses — this alias vanishes the moment you close the terminal.

    Both the export and the alias above 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 .sh file 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 ~/.bashrc re-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?

  6. 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 -h
    Free space per filesystem. Find the one mounted on / and note its Use% — is anything dangerously full?
    df -i
    The same filesystems by inode usage. Compare IUse% against the Use% from above — they can differ wildly, and that difference is the whole point.
    du -sh /etc 2>/dev/null
    Total size of the entire config directory. 2>/dev/null discards any 'permission denied' noise so you get one clean total.
    type -a ls
    Reveals everything ls resolves to — likely an alias plus the /usr/bin/ls binary. Confirms what actually runs when you type the command.
    alias
    Lists every alias currently defined in your shell. These are the shortcuts loaded from your startup files — your environment, made visible.
Recap
  • vi is modal: you're always in command mode or insert mode, and the same key means different things in each. When confused, press Esc first, then act.
  • The FHS makes file locations predictable: /etc for config, /var for changing data, /usr for programs, /bin for everyone vs /sbin for admins.
  • Match the search tool to the question: which/type for PATH commands, find for live (always-current) filesystem walks, locate for fast indexed lookups that can be stale.
  • Disk trouble has two forms: df -h (bytes) and df -i (inodes) — you can exhaust either independently. du -sh finds the heavy directory, and fsck only runs on an unmounted filesystem.
  • Settings typed at the prompt are temporary: export makes a variable visible to child processes, and writing it to ~/.bashrc or /etc/profile.d/*.sh makes it survive new sessions.
Command → what it does / what to watch for
CommandAnswers / doesWatch for
i / a / oEnter insert mode in viYou're now typing TEXT, not commands
EscReturn to command modeYour universal 'I'm stuck' escape
:wq / :q!Save+quit / quit discarding changesMust be in command mode (Esc first)
type -a cmdWhat a command resolves toSees aliases that which misses
find <dir> -nameLive filesystem searchAlways current; slower
locate <name>Indexed filename searchFast but STALE — run updatedb
df -hFree space (bytes)High Use% breaks writes
df -iFree inodes (file count)Can hit 100% with space free
du -sh <dir>Total size of a directoryFinds the space hog
fsck <device>Repair filesystem structureUNMOUNT first or risk corruption
export VAR=valMake variable inheritableWithout it, children see nothing
>> ~/.bashrcPersist alias/var per userUse >> not >; /etc/profile.d for all users