LPIC-1 command cheat sheet
The commands 101-500 and 102-500 ask for, grouped by LPI topic number and shown in both package families where the two differ. Every output is from a real session on this path's own two machines - one Debian-family, one RPM-family.
- Debian familyUbuntu 26.04 LTS - kernel 7.0.0-30-generic, systemd 259
- RPM familyAlmaLinux 10.2 - kernel 6.12.0-211.7.3.el10_2, systemd 257
- Shellzsh 5.9 login shell on both - bash 5.3.9 (Ubuntu) / 5.2.26 (AlmaLinux)
- Debian packagingdpkg 1.23.7, apt 3.2.0
- RPM packagingrpm 4.19.1.1, dnf 4.20.0
- Commands34
Topic 102 - Installation and package management
-
dpkg -l | grep -c '^ii'Count installed packages. The `ii` state field is what `dpkg -l` leads with, and it matters again when a package reaches `rc`.
bash Example session dpkg -l | grep -c '^ii'; echo "--- packages installed (dpkg)"; dpkg -l | sed -n '6,8p'740--- packages installed (dpkg)ii 3cpio 0.14.0-1ubuntu1 amd64 Manage initrd cpio archivesii adduser 3.153ubuntu1 all add and remove users and groupsii amd64-microcode 3.20251202.1ubuntu2 amd64 Platform firmware and microcode for AMD CPUs and SoCs -
rpm -qa | wc -lThe same count on the RPM family. Do not compare the two numbers - RPM splits software into far more, smaller packages.
bash Example session rpm -qa | wc -l; echo "--- packages installed (rpm)"; rpm -qa --last | head -31266--- packages installed (rpm)tmux-3.3a-13.20230918gitb202a2f.el10.x86_64 Sat Aug 29 14:39:41 2026tldr-3.3.0-3.el10_0.noarch Sat Aug 29 14:39:38 2026python3-termcolor-2.5.0-1.el10_0.noarch Sat Aug 29 14:39:38 2026 -
dpkg -S <file>Which package owns a file. Prints `package: path`.
bash Example session echo "which package owns /usr/bin/ssh?"; dpkg -S /usr/bin/sshwhich package owns /usr/bin/ssh?openssh-client: /usr/bin/ssh -
rpm -qf <file>The same question on RPM. Prints the full name-version-release-architecture.
bash Example session echo "which package owns /usr/bin/ssh?"; rpm -qf /usr/bin/sshwhich package owns /usr/bin/ssh?openssh-clients-9.9p1-23.el10_2.alma.1.x86_64 -
dpkg -L <pkg> / dpkg-query -W -f='${Conffiles}\n' <pkg>What a package contains, and which of those files are configuration. Debian records an md5 per conffile so it knows at upgrade time whether you edited it.
bash Example session dpkg -L vsftpd | grep -E '^/etc|bin/' | head -5; echo "--- and which are conffiles:"; dpkg-query -W -f='${Conffiles}\n' vsftpd | head -3/etc/etc/ftpusers/etc/init.d/etc/init.d/vsftpd/etc/logrotate.d--- and which are conffiles: /etc/ftpusers 839f3157aad792bafbbdcd932a95a345 /etc/init.d/vsftpd e919b8278d7f2c5cb8ad96071f9ba3be /etc/logrotate.d/vsftpd dac2cb7b9cfd8a03b4fa9ca3601a43a6 -
rpm -ql <pkg> / rpm -qc <pkg>The same pair on RPM. `-ql` lists files only - no directories, unlike `dpkg -L`.
bash Example session rpm -ql vsftpd | grep -E '^/etc|bin/' | head -5; echo "--- and which are config:"; rpm -qc vsftpd/etc/logrotate.d/vsftpd/etc/pam.d/vsftpd/etc/vsftpd/etc/vsftpd/ftpusers/etc/vsftpd/user_list--- and which are config:/etc/logrotate.d/vsftpd/etc/pam.d/vsftpd/etc/vsftpd/ftpusers/etc/vsftpd/user_list/etc/vsftpd/vsftpd.conf -
dpkg -r <pkg>Remove but KEEP configuration. The package lands in state `rc`, which is what `dpkg -l | grep '^rc'` finds.
bash Example session sudo dpkg -r vsftpd 2>&1 | tail -1; dpkg -l vsftpd | tail -1; echo "--- state rc: Removed, Config-files still on disk"Processing triggers for man-db (2.13.1-1build1)…rc vsftpd 3.0.5-0.4 amd64 lightweight, efficient FTP server written for security--- state rc: Removed, Config-files still on disk -
dpkg -P <pkg>Purge - removes the configuration too. RPM has no equivalent verb; `rpm -e` leaves an edited config as `.rpmsave`.
bash Example session sudo dpkg -P vsftpd 2>&1 | tail -1; dpkg -l vsftpd 2>&1 | tail -1; ls /etc/vsftpd.conf 2>&1 | tail -1; echo "--- purge took the config with it"debconf: falling back to frontend: Noninteractivedpkg-query: no packages found matching vsftpdls: cannot access '/etc/vsftpd.conf': No such file or directory--- purge took the config with it -
rpm -V <pkg>Verify installed files against the package manifest. Silence means every file matches - test the exit status, not the output.
bash Example session sudo dnf install -y tree >/dev/null 2>&1; rpm -V tree; echo "rpm -V exit: $? - silence means every file matches what the package recorded"rpm -V exit: 0 - silence means every file matches what the package recorded -
rpm -V <pkg> (a failure)`S.5....T.` reads as size, MD5 and mtime all differing. A dot is a check that passed, so the string tells you what changed before you open the file.
bash Example session sudo truncate -s -1 /usr/bin/tree && rpm -V tree; echo "--- S size, 5 checksum, T mtime: the binary no longer matches"S.5....T. /usr/bin/tree--- S size, 5 checksum, T mtime: the binary no longer matches
Topic 103 - GNU and Unix commands
-
cmd 2>&1 | ...A pipe carries stdout only. `2>&1` is what puts stderr into it - which is why `cmd | grep x` sometimes misses text you can plainly see.
bash Example session cd ~/lpic1/streams && ls auth.log missing.log 2>&1 | grep -c .; echo "--- 2>&1 put both into the pipe: 2 lines"2--- 2>&1 put both into the pipe: 2 lines -
cmd > file 2>&1 (not: 2>&1 > file)Order matters. `> file 2>&1` sends both to the file; `2>&1 > file` points stderr at the terminal FIRST, then moves stdout, so the error escapes.
bash Example session cd ~/lpic1/streams && ls auth.log missing.log > out.txt 2>&1; echo "both in the file:"; cat out.txtboth in the file:ls: cannot access 'missing.log': No such file or directoryauth.log -
cmd | tee file | nextWrite to a file and keep the stream going. `sudo tee` is also how you write to a root-owned file, because `sudo echo x > file` redirects as YOU.
bash Example session cd ~/lpic1/streams && grep FAILED auth.log | tee failures.txt | wc -l; echo "--- the count, and the lines are also in failures.txt:"; cat failures.txt3--- the count, and the lines are also in failures.txt:bob 2026-08-01 login FAILEDbob 2026-08-02 login FAILEDbob 2026-08-03 sudo FAILED -
grep -E 'a|b' · grep '^x' · grep 'x$'Alternation needs `-E`; in basic regex `|` is literal. `^` and `$` are the two anchors, and both are examined by name.
bash Example session cd ~/lpic1/streams && grep -E 'alice|carol' auth.log; echo "--- alternation needs -E, or escaped pipes in basic regex"alice 2026-08-01 login okalice 2026-08-02 login okcarol 2026-08-02 sudo ok--- alternation needs -E, or escaped pipes in basic regex -
cut -d" " -f1 file | sort | uniq -c | sort -rnThe frequency-count idiom. `uniq` only collapses ADJACENT duplicates, so the `sort` before it is not optional.
bash Example session cd ~/lpic1/streams && cut -d' ' -f1 auth.log | sort | uniq -c | sort -rn; echo "--- who appears most" 3 bob 2 alice 1 dave 1 carol--- who appears most -
awk '$4=="X" {print $1}' fileWhen the test is about one specific field. `$0` is the whole line, `NF` the field count.
bash Example session cd ~/lpic1/streams && awk '$4=="FAILED" {print $1}' auth.log | sort -u; echo "--- who has ever failed"bob--- who has ever failed -
sed 's/old/new/' file · sed -n '2,4p' fileSubstitution on the way past, and line selection with `-n`. sed does NOT edit the file unless you pass `-i`.
bash Example session cd ~/lpic1/streams && sed 's/FAILED/DENIED/' auth.log | grep DENIED; echo "--- sed substitutes on the way past"bob 2026-08-01 login DENIEDbob 2026-08-02 login DENIEDbob 2026-08-03 sudo DENIED--- sed substitutes on the way past -
ls | xargs cmd · find -print0 | xargs -0 cmdFor commands that take arguments, not stdin. Use the NUL-separated pair when filenames may contain spaces.
bash Example session cd ~/lpic1/streams && ls *.txt | xargs wc -l; echo "--- wc takes filenames as arguments, not on stdin" 3 failures.txt 2 out.txt 1 out2.txt 6 total--- wc takes filenames as arguments, not on stdin
Topic 104 - Devices, filesystems and the FHS
-
findmnt -no SOURCE,TARGET / /bootWhich device is the system disk. Run this BEFORE anything destructive - device names shift when disks are added.
bash Example session findmnt -no SOURCE,TARGET / /boot; echo "--- the system disk on THIS machine is sde"--- the system disk on THIS machine is sde -
parted -s <disk> mklabel gpt / mkpart primary ext4 1MiB 100%GPT label and one aligned partition. Starting at 1MiB aligns to the erase block; `-s` is script mode.
bash Example session sudo parted -s /dev/sda mklabel gpt && sudo parted -s /dev/sda mkpart primary ext4 1MiB 100% && sudo parted -s /dev/sda printModel: Msft Virtual Disk (scsi)Disk /dev/sda: 5369MBSector size (logical/physical): 512B/4096BPartition Table: gptDisk Flags: Number Start End Size File system Name Flags 1 1049kB 5368MB 5367MB primary -
mkfs.ext4 -L <label> <part> · blkid <part>Make the filesystem and read back its UUID and label - the two identifiers fstab can use.
bash Example session sudo mkfs.ext4 -q -L lpic1data /dev/sda1 && sudo blkid /dev/sda1/dev/sda1: LABEL="lpic1data" UUID="76280fa8-af11-4d78-b1dd-97850c7b15ff" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="primary" PARTUUID="cc5018a1-d1a9-4d38-9423-008348157fb0" -
tune2fs -l <part>Filesystem metadata. `Reserved block count` is 5% by default, which is why a fresh 4.9 G filesystem shows 4.6 G free.
bash Example session sudo tune2fs -l /dev/sda1 | grep -E 'Filesystem volume name|Block count|Reserved block count|Filesystem features|Default mount options'Filesystem volume name: lpic1dataFilesystem features: has_journal ext_attr resize_inode dir_index orphan_file filetype extent 64bit flex_bg metadata_csum_seed sparse_super large_file huge_file dir_nlink extra_isize metadata_csumDefault mount options: user_xattr aclBlock count: 1310208Reserved block count: 65510 -
UUID=<uuid> /mnt/x ext4 defaults,noatime 0 2The six fstab fields: device, mount point, type, options, dump, pass. `pass` is 1 for root, 2 for others, 0 to skip.
bash Example session UU=$(sudo blkid -s UUID -o value /dev/sda1); echo "UUID=$UU"; echo "UUID=$UU /mnt/lpic1data ext4 defaults,noatime 0 2" | sudo tee -a /etc/fstab >/dev/null && tail -1 /etc/fstabUUID=76280fa8-af11-4d78-b1dd-97850c7b15ffUUID=76280fa8-af11-4d78-b1dd-97850c7b15ff /mnt/lpic1data ext4 defaults,noatime 0 2 -
mount -aALWAYS run this before rebooting after an fstab edit. A malformed line can stop the machine reaching a login prompt.
bash Example session sudo mount -a && df -h /mnt/lpic1data | tail -1; echo "--- mount -a read fstab and mounted it"/dev/sda1 4.9G 1.3M 4.6G 1% /mnt/lpic1data--- mount -a read fstab and mounted it -
ls /dev/disk/by-uuid/ · ls /dev/disk/by-id/`by-uuid` follows the FILESYSTEM, `by-id` follows the HARDWARE. fstab wants the first; a script addressing a physical disk wants the second.
bash Example session ls -l /dev/disk/by-uuid/ | grep sda1; echo "--- the UUID is a property of the FILESYSTEM, not the slot"lrwxrwxrwx 1 root root 10 Aug 29 11:31 76280fa8-af11-4d78-b1dd-97850c7b15ff -> ../../sda1--- the UUID is a property of the FILESYSTEM, not the slot -
umount <part> && fsck -f -y <part>Unmount FIRST - always. `-f` forces the check past the clean flag. Exit 0 is clean, 1 means errors were found and corrected.
bash Example session sudo umount /mnt/lpic1data && sudo fsck -f -y /dev/sda1 2>&1 | tail -6Pass 1: Checking inodes, blocks, and sizesPass 2: Checking directory structurePass 3: Checking directory connectivityPass 4: Checking reference countsPass 5: Checking group summary informationlpic1data: 12/327680 files (0.0% non-contiguous), 42397/1310208 blocks
Topic 107 - Administrative tasks
-
useradd -m -c "<comment>" -s <shell> <user>Creates the account, the home directory from /etc/skel, and a private group. `useradd -D` shows the defaults it used.
bash Example session sudo useradd -m -c "LPIC lab user" -s /bin/bash lpicdemo && getent passwd lpicdemo; echo "--- name:x:uid:gid:comment:home:shell"lpicdemo:x:1001:1001:LPIC lab user:/home/lpicdemo:/bin/bash--- name:x:uid:gid:comment:home:shell -
usermod -aG <group> <user>The `-a` is not optional. `usermod -G` without it REPLACES every secondary group the user had.
bash Example session sudo groupadd lpicops && sudo usermod -aG lpicops lpicdemo && id lpicdemouid=1001(lpicdemo) gid=1001(lpicdemo) groups=1001(lpicdemo),1002(lpicops) -
chage -M <days> -W <days> <user> · chage -l <user>Maximum password age and warning period. The default max of 99999 is not "disabled", it is 273 years.
bash Example session sudo chage -M 30 -W 7 lpicdemo && sudo chage -l lpicdemo | grep -E 'Maximum|Warning'Maximum number of days between password change : 30 -
passwd -l <user> · passwd -u <user>Prepends `!` to the hash. Does NOT block key-based SSH - for that, set the shell to nologin or expire the account.
bash Example session sudo passwd -l lpicdemo >/dev/null 2>&1; sudo getent shadow lpicdemo | cut -d: -f2 | cut -c1-3; echo "--- a locked password starts with ! - the account cannot authenticate by password"!--- a locked password starts with ! - the account cannot authenticate by password -
crontab -u <user> -l · crontab -eFive fields: minute, hour, day-of-month, month, day-of-week. `crontab -r` deletes the lot with no confirmation.
bash Example session echo '*/5 * * * * /usr/bin/true # lpic demo job' | sudo crontab -u lpicdemo - && sudo crontab -u lpicdemo -l*/5 * * * * /usr/bin/true # lpic demo job -
/etc/crontab and /etc/cron.d (SIX fields)System crontabs carry an extra USER column between the schedule and the command. A user crontab has no such field.
bash Example session head -6 /etc/crontab; echo "--- the system crontab has an extra field: the user to run as"# /etc/crontab: system-wide crontab# Unlike any other crontab you don't have to run the `crontab'# command to install the new version when you edit this file# and files in /etc/cron.d. These files also have username fields,# that none of the other crontabs do. --- the system crontab has an extra field: the user to run as -
systemctl list-timers --allWhat the machine actually schedules. Shows NEXT and LAST, which cron cannot tell you at all.
bash Example session systemctl list-timers --all --no-pager | head -5NEXT LEFT LAST PASSED UNIT ACTIVATESSat 2026-08-29 11:40:00 UTC 5min Sat 2026-08-29 11:30:09 UTC 4min 11s ago sysstat-collect.timer sysstat-collect.serviceSat 2026-08-29 11:41:09 UTC 6min Sat 2026-08-29 10:28:40 UTC - fwupd-refresh.timer fwupd-refresh.serviceSat 2026-08-29 20:46:00 UTC 9h Sat 2026-08-29 08:25:25 UTC - motd-news.timer motd-news.serviceSun 2026-08-30 00:00:00 UTC 12h Sat 2026-08-29 08:25:25 UTC - dpkg-db-backup.timer dpkg-db-backup.service -
systemd-analyze calendar "<OnCalendar expr>"Verify a schedule before trusting it. There is no cron equivalent - a mistyped cron line is discovered when the job does not run.
bash Example session systemd-analyze calendar "*-*-* 06:00:00" 2>&1 | head -4; echo "--- systemd will tell you when a schedule actually fires"Normalized form: *-*-* 06:00:00 Next elapse: Sun 2026-08-30 06:00:00 UTC From now: 18h left--- systemd will tell you when a schedule actually fires
No command matches that search.