find, locate and the two kinds of link
find is the tool the exam reaches for whenever a task says "all files that...", and its two -exec forms behave differently enough to matter. This guide covers searching by name, type, size, time and permission, the difference between ; and +, locate and why its answer can be wrong, and the two kinds of link - including what happens to each when the original is deleted.
Essential Commands Guide 10 of 38 Intermediate
- OSUbuntu 26.04 LTS
- LVM2.03.31 (Ubuntu) / 2.03.36 (AlmaLinux)
- nftables1.1.6 (Ubuntu) / 1.1.5 (AlmaLinux)
- TimeAbout 20 min
GNU findutils. plocate is not installed on Ubuntu 26.04 by default - the capture installs it. -printf and -xtype are GNU extensions and are on both exam images.
- Firewallufw 0.36.2 enabled but reporting inactive / firewalld active
- Network confignetplan + systemd-networkd / NetworkManager 1.56.0
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| LFCS-A01 | 192.168.0.70 | Ubuntu 26.04 LTS | Primary host - most guides run only here | 2 Core | 4 GB | 50 GB |
This guide includes
Use this because find is what the exam reaches for whenever a task says "all files that...". This matters because -exec ; and -exec + run the same command completely differently - once per file, or once for all of them.
- searching by name, type, size, time and permission
- seeing the difference between
-exec ;and-exec + - using
-print0for filenames you did not choose - using locate, which reads an index instead of the disk
- comparing the two kinds of link, what happens when the original goes, and what a hard link cannot do
Before you start
-
find, by name and by type
A small tree, searched four ways.
bash Example session cd ~/ess/f && find . -name '*.log' | sort./app/logs/big.log./app/logs/old.log./app/logs/small.logcd ~/ess/f && find . -type d | sort; echo "---"; find . -type f | wc -l; echo "---"; find . -iname '*.LOG' | wc -l../app./app/conf./app/logs./tmp---5---3cd ~/ess/f && find . -maxdepth 2 -type f | sort; echo "--- maxdepth stops it walking the whole tree"./tmp/scratch--- maxdepth stops it walking the whole treecd ~/ess/f && find . -name '*.log' -not -name 'old*' | sort; echo "---"; find . \( -name '*.conf' -o -name '*.log' \) -type f | wc -l./app/logs/big.log./app/logs/small.log---4Expected resultThree
.logfiles; five directories;-maxdepth 2finding only./tmp/scratch; and-notexcludingold.log.Success conditionYou can find files by name and shape.
-
By size, by time, by permission
The three tests that answer real questions - and one that finds nothing.
bash Example session cd ~/ess/f && find . -type f -size +1k | sort; echo "--- and the sizes for comparison:"; find . -type f -printf '%6s %p\n' | sort -rn--- and the sizes for comparison: 292 ./app/logs/big.log 2 ./app/logs/small.log 2 ./app/conf/app.conf 0 ./tmp/scratch 0 ./app/logs/old.logcd ~/ess/f && find . -mtime +5 -type f; echo "--- older than 5 days"; find . -mtime -1 -type f | wc -l; echo "--- modified in the last day"./app/logs/old.log--- older than 5 days4--- modified in the last daycd ~/ess/f && find . -type f -perm 600; echo "--- exactly 600"; find . -type f -perm -u=w | wc -l; echo "--- has at least user-write"./app/conf/app.conf--- exactly 6005--- has at least user-writecd ~/ess/f && find . -newer app/logs/small.log -type f | sort; echo "--- newer than a reference file"./tmp/scratch--- newer than a reference fileExpected result
-size +1kmatching nothing (the biggest file is 292 bytes);-mtime +5finding the 10-day-old file;-perm 600finding the config.Success conditionYou can find files by their metadata.
-
-exec, and the difference between ; and +
The same search, running the same command two ways.
bash Example session cd ~/ess/f && find . -name '*.log' -exec wc -l {} \; | sort -rn100 ./app/logs/big.log1 ./app/logs/small.log0 ./app/logs/old.logcd ~/ess/f && find . -name '*.log' -exec wc -l {} + ; echo "--- with + it runs ONCE with all the arguments, so wc prints a total" 0 ./app/logs/old.log100 ./app/logs/big.log 1 ./app/logs/small.log101 total--- with + it runs ONCE with all the arguments, so wc prints a totalExpected resultWith
\;: three separatewcruns and no total. With+: one run and a101 totalline.Success conditionYou can choose the right -exec form.
-
-print0, for the filenames you did not choose
Handing find's results to another command safely.
bash Example session cd ~/ess/f && find . -name 'scratch' -delete && find . -name 'scratch' | wc -l; echo "--- -delete needs no -exec rm"0--- -delete needs no -exec rmcd ~/ess/f && find . -type f -name '*.log' -printf '%p %TY-%Tm-%Td %s\n' | sort./app/logs/big.log 2026-08-29 292./app/logs/old.log 2026-08-19 0./app/logs/small.log 2026-08-29 2Expected result
-deleteremoving the file with no-exec; and-printfproducing path, date and size in one line each.Success conditionYou can pass results to another tool without breaking on spaces.
-
locate, which reads an index instead of the disk
Not installed by default. Then indexed, then queried - and then caught out.
bash Example session command -v locate plocate mlocate updatedb 2>/dev/null; echo "--- (nothing above means not installed)"/usr/bin/locate/usr/bin/plocate/usr/bin/updatedb--- (nothing above means not installed)sudo apt-get install -y -qq plocate 2>&1 | tail -1; command -v locate updatedb/usr/bin/locate/usr/bin/updatedbsudo updatedb && locate -n 3 nginx.conf 2>&1 | head -4; echo "---"; locate -c '*.service' 2>&1 | tail -1---532cd ~/ess/f && touch brand-new-file.txt && locate brand-new-file.txt 2>&1 | head -2; echo "--- not in the index yet, because the index is a snapshot"; sudo updatedb && locate brand-new-file.txt | head -1--- not in the index yet, because the index is a snapshot/home/sysadmin/ess/f/brand-new-file.txtExpected resultNothing installed at first; then
locate -c '*.service'counting 554; and a brand-new file not found untilupdatedbruns again.Success conditionYou know when to trust locate and when not to.
-
The two kinds of link
A hard link and a symlink to the same file, side by side.
bash Example session cd ~/ess/f && printf 'original\n' > target.txt && ln target.txt hard.txt && ln -s target.txt soft.txt && ls -li target.txt hard.txt soft.txt789207 -rw-rw-r-- 2 sysadmin sysadmin 9 Aug 29 09:41 hard.txt789208 lrwxrwxrwx 1 sysadmin sysadmin 10 Aug 29 09:41 soft.txt -> target.txt789207 -rw-rw-r-- 2 sysadmin sysadmin 9 Aug 29 09:41 target.txtcd ~/ess/f && stat -c '%h links, inode %i, %F, %n' target.txt hard.txt soft.txt2 links, inode 789207, regular file, target.txt2 links, inode 789207, regular file, hard.txt1 links, inode 789208, symbolic link, soft.txtExpected result
target.txtandhard.txtsharing inode 403889 with a link count of 2;soft.txton its own inode, 1 link, and 10 bytes - the length of the target's name.Success conditionYou can tell the two apart from
ls -li. -
And what happens when the original goes
Delete
target.txtand ask both links for the data.bash Example session cd ~/ess/f && rm target.txt && echo "--- target removed:"; cat hard.txt; echo "--- the hard link still has the data"; cat soft.txt 2>&1 | tail -1; echo "--- the symlink is now dangling"--- target removed:original--- the hard link still has the datacat: soft.txt: No such file or directory--- the symlink is now danglingcd ~/ess/f && ls -l soft.txt; find . -xtype l; echo "--- find -xtype l lists broken symlinks"lrwxrwxrwx 1 sysadmin sysadmin 10 Aug 29 09:41 soft.txt -> target.txt./soft.txt--- find -xtype l lists broken symlinkscd ~/ess/f && ln -s /etc/hostname abs.txt && readlink abs.txt; readlink -f abs.txt; echo "--- readlink -f resolves the whole chain"/etc/hostname/etc/hostname--- readlink -f resolves the whole chainExpected result
cat hard.txtprintingoriginal;cat soft.txtgivingNo such file or directory; andfind -xtype llisting./soft.txt.Success conditionYou can find and reason about broken links.
-
And what a hard link cannot do
Two limits, both of which the symlink does not have.
bash Example session cd ~/ess/f && ln app/logs/big.log hardsame 2>&1 | tail -1 && echo "same filesystem: fine"; ln /etc/hostname /tmp/../home/sysadmin/ess/f/hardcross 2>&1 | tail -1; ln -s /etc/hostname softcross && echo "and a symlink crosses anything"same filesystem: fineln: failed to create hard link '/etc/hostname' => '/tmp/../home/sysadmin/ess/f/hardcross': Permission deniedand a symlink crosses anythingcd ~/ess/f && ln app 2>&1 | tail -1; ln -s app dirsoft && ls -ld dirsoft; echo "--- you cannot hard-link a directory, but you can symlink one"ln: app: hard link not allowed for directorylrwxrwxrwx 1 sysadmin sysadmin 3 Aug 29 09:41 dirsoft -> app--- you cannot hard-link a directory, but you can symlink oneExpected resultA hard link within the filesystem working; a hard link to a directory refused; and a symlink doing both.
Success conditionYou know which kind of link to reach for.
Troubleshooting
find . -name *.logsearches for the wrong thing.Why: The shell expanded the pattern before find saw it.
Fix:Quote it:
-name '*.log'.find -size +1Mfinds nothing you expected.Why: The threshold is wrong, or the suffix was omitted and it is counting 512-byte blocks.
Fix:Always give a suffix -
c,k,M,G. Check real sizes with-printf '%s %p\n'.-mtime 5misses the files you wanted.Why: Plain
5means exactly the 5th day, not "within 5 days".Fix:
-mtime -5for newer than,+5for older than.-execis very slow on a large tree.Why:
\;spawns one process per file.Fix:
-exec cmd {} +, with{}immediately before the+.A pipeline from find breaks on some filenames.
Why: Whitespace or newlines in names.
Fix:
-print0 | xargs -0, or-exec ... +.locatecannot find a file that exists.Why: The index predates the file.
Fix:
sudo updatedb, or usefindwhen currency matters.ln: failed to create hard link: Invalid cross-device link.Why: Source and destination are on different filesystems.
Fix:
ln -sfor a symlink, or copy it.Deleting a large log did not free any disk space.
Why: A process still holds the file open, so the inode is not released.
Fix:
lsof | grep deletedto find the holder, then restart it or truncate with: > fileinstead.