Writing Criteria
Every guide in this repo is written to the same standard. If you're adding or editing a guide — human or AI — hold it to these.
- Plain words, real ideas. Simple language, never dumbed-down concepts. Explain jargon on first use; collect terms in the glossary.
- No hype. No marketing adjectives, no "revolutionary," no promises about what something will do for the reader.
- Honest about limits. Say what the thing can't do. State uncertainty instead of smoothing it over. Validation notes include what remains unverified.
- No invented numbers. Benchmarks, defaults, and specs are either measured or qualified with "depends on your setup." If it wasn't tested, say so.
- Checkable. Commands are meant to be run, with "Check:" blocks showing what success looks like. Sources are linked, not asserted.
- Respect the reader's setup. Call out Windows/Mac/Linux differences. Don't assume a GPU, a budget, or prior experience.
- Safety isn't skipped. Real risks get stated plainly, not buried.
- Scoped. Each guide says what it covers and points elsewhere for the rest. No guide pretends to be the whole subject.
- Structure over prose. Tables, before/after examples, symptom → fix. Scannable beats essay.
Verification notes for this guide
Standard 3 and 4 applied to this guide,
specifically. The build machine ran Ubuntu
with GNU findutils 4.9.0 — both find
and xargs, the only tools this guide
teaches — plus GNU coreutils ls/sort
9.4. Every quoted output in the chapters is
a capture from those tools, run against the
specimen tree. Working transcripts are kept
with the build
(finding-files-lab/transcripts/).
The lab. One tree, built by
examples/build-tree.sh, identical on every
rebuild: 51 entries (39 files, 10
directories, 2 symlinks). The specimens are
named in examples/lab-notes.txt: eleven
files of exact byte counts (0 through one
byte over 1 MiB), five files of exact
relative ages (0 minutes to 49 hours), two
archive logs backdated to 2023 and 2021,
mixed-case extensions, a space-bearing name
(meeting notes.txt), an apostrophe-bearing
one (o'brien.txt), a good symlink and a
broken one, an empty file, an empty
directory, and four files whose permission
bits the script sets explicitly (644, 600,
666, 4755). Two fixtures are made by the
chapters themselves: globtrap/ (chapter 2)
and tree-del/, a second build of the tree
that chapters 8 and 10 delete from. A
two-file ownership fixture lives in
/tmp/owners for reasons stated below.
Verified by running, per chapter:
- Ch. 1: both
--versionoutputs; the unfiltered walk of the tree (all 51 entries); counts by-type(39 f, 10 d, 2 l); a file as starting point; find with no path, walking.. - Ch. 2:
-name '*.txt'(15 matches),-name '*.TXT'(1),-iname '*.txt'(16);-name '*.jpg'(1) vs-iname(2); the?demos???.bin(exactly the three 3-digit sizes);-path '*/photos/*'(4 entries);-name 'raw/*'(no output, exit 0);-name '.*'(the dotfile only);-type dand-type lin full; the glob trap both ways —echo *.txt's expansion in the tree's top level, the unquoted find erroring with GNU's own hint (exit 1), and the one-matchglobtrapform silently returning only./solo.txtwhere the quoted form returns both files. - Ch. 3: the
ls -lshelf;-size 1M(9 files plus the directory itself),-size -1M(the empty file only),-size +1M(the one-byte-over file only, the exact-MiB file excluded),-size 1048576cand+1048576c(the same two files separated exactly),-size 1(1–512 bytes, 3 files plus the directory),-size 1k(1–1024, 6 files plus the directory);-size +1Macross the tree (2 entries); the sparse trio —ls -l2500000,du -h0,-printf2500000 — onbig.log. - Ch. 4: the
ls -ltcast listing;-mmin -60(2 files plus the directory) and-mmin +60(3);-mtime 0(3 files plus the directory, the 2-hour marker included),-mtime 1(the 25-hour file),-mtime 2and-mtime +1(the 49-hour file only, proving the discarded fraction);-neweragainst the marker (the two younger files);-mtime +365over the archive (both backdated logs). - Ch. 5: the
/tmp/ownerslisting and-user root/-user nobody/-not -user rootover it; thetree/permslisting;-perm -0002,-perm -4000,-perm /6000,-perm 600,-perm 644, each returning exactly the one specimen built for it; and thechownrefusal on the build machine's home filesystem, captured verbatim with its exit 1 (see Documented-only for why the ownership captures live in /tmp). - Ch. 6: the AND chain
-type f -name '*.log'(4); the OR of name tests (2, the uppercase JPG absent as the text says);-not -type dover photos (3); the trap in full —-name '*.txt' -o -name '*.md' -printprinting exactly one line against the 15-file count bywc -l— and the parenthesised fix printing 16. - Ch. 7:
-maxdepth 1in full (21 entries);-maxdepth 2 -name '*.CR2'(silence, exit 0) against the unlimited search (the file, at depth 3);-mindepth 2over photos (1);-name '*.tmp'(the 2 cache files); the canonical prune printing 37 files with no cache entry; and the-o-less prune printing nothing at all, exit 0. - Ch. 8:
-lsover the four logs (inode, blocks —big.logat 0 — and dates all as printed); the-printrehearsal;-exec echo … \;(two invocations, two lines) against-exec echo … +(one line, both names);-exec ls -l {} +over both jpgs; the-okrun fedythenn(first executed, second skipped, prompts as printed); and on the expendable copy only:-deleteof the.tmppair, proven by re-running the rehearsal, theemptydirdeletion, and the refusal on the non-emptyphotos(real message, exit 1). The original tree was re-checked intact afterwards. - Ch. 9:
getconf ARG_MAX(2097152);/bin/true $(seq 1 500000)refused ("Argument list too long", exit 126);seq 1 500000 | xargs -n 10000 echo | wc -l(exactly 50 — the count cap binding), and with-n 100000exactly 26 — the size cap binding first, as the chapter says;-n 2pairing; the naive pipeline in full (the quote-parsing error, the two split-path errors, 13 files listed, exit 123); the-print0 | xargs -0run listing all 15 with both hostile names intact, exit 0;-I{}against plain xargs (three invocations against one). - Ch. 10: the largest-files pipeline
(top 5 as printed); the last-hour
search scoped to
times/;-empty(all three specimens);-type l(2) against-xtype l(the broken one only); the old-log cleanup on a fresh copy — rehearsal naming both backdated logs,-delete, andlsprovingrecent.logsurvived. - Ch. 11:
locateinvoked and absent (exit 127);fdfind --version(9.0.0);fdfind img(3 results, case-insensitive by smart case);fdfind txt(15) set againstfind -iname '*txt*'(16), the difference being exactly the dotfile, as the chapter claims. - Ch. 12: the missing starting point
(message, exit 1); the no-match silence
(
-name '*.zzz', exit 0); and the permission-denied walk run asnobodyagainst a mode-700 directory — the starting point printed, the denial reported, exit 1.
Capture hygiene (declared trims): some
long listings are shown shortened in the
chapters, always with the count stated in
the text or a bracketed note where the cut
falls: the find tree walk in chapter 1
(51 entries), the -iname listing in
chapter 2, the prune listing in chapter 7
(37 lines), the two ls -l listings in
chapter 9 (13 and 15 files), and the two
fdfind/find listings in chapter 11 (15
and 16). Nothing was cut silently; the full
captures are in the transcripts. Two quoted
error lines carry the build shell's own
prefix (bash: line 1: …, for the ARG_MAX
refusal and the missing locate) because
the shells that produced them reported
themselves; the chapters quote them as
captured. Transcript [exit N] annotations
are quoted in the chapters only where the
exit code is itself the lesson. The one
mid-task correction: an early xargs capture
mixed a corrected display string with the
uncorrected command; it was retaken in
full, and both -n runs above are the
retaken ones.
Build-machine weather, stated once: the
lab's home filesystem is an overlay that
assigns new files group nogroup and, with
the workspace's umask of 007, mode 660, on
setgid directories of mode 2770. Listing
modes and groups in the chapters are that
machine's weather, except where the build
script sets bits explicitly (the whole of
tree/perms/, which is why the -perm
captures are exact). Ages in ls output
are the capture day's; the script ages
specimens relative to whenever it runs.
Documented-only (not run), and why:
- locate, mlocate/plocate, and updatedb: not installed on the build machine — the absence is captured (exit 127) and the database behaviour in chapter 11 is described from the tools' documentation. Nothing about locate's output is quoted.
- fd's wider language:
fdfind9.0.0 was present and two searches were captured to show its shape and defaults. Its.gitignorehandling is described, not demonstrated (the lab tree is not a git checkout), and its full option set is a scope decision, recorded here: named, placed, not taught. - xargs
-P(parallel runs): named in chapter 9, not run — parallel output is interleaved and unordered by design, so there is no stable capture to quote, and nothing in the guide depends on it. - The other timestamp tests:
-atime,-ctime,-amin,-cmin,-newermt, and-daystartare named with their meanings; the mtime family is the one exercised. atime on the build machine is relatime-governed, so a capture would teach the filesystem's laziness, not the test. - Symbolic
-permspellings (-perm -o+wand kin): noted in chapter 5 as a GNU provision; the octal forms are the ones captured, being the spellings that read identically across finds. - BSD and macOS differences: GNU-only
provisions used in this guide are
-printf,-print0,-xtype, and the/form of-perm(BSDfindspells the any-of test differently and older BSDs lack-printf). These contrasts are stated from the BSD tools' documentation; no BSD system was available to the build. Everything else taught is the common core, run under GNU find. - Changing ownership: the build
machine's home overlay refuses
chownoutright, even to root (captured in chapter 5). The build script still attempts it under|| true, so on a filesystem that permits it thenot-mine.txtspecimen belongs tonobodyaslab-notes.txtdescribes; on the build machine it remained the builder's, and the-usercaptures were made on the/tmpfixture, whose filesystem permits the change. - Installation on a reader's machine is described, not performed; findutils was already present.
Specimen discipline, for the record: every name in the tree is invented, every address is nobody's, the setuid specimen is a three-line shell script that is never executed by any chapter, and the only deletions in the guide happen inside purpose-built copies of the lab tree. No command in the guide was aimed at a real system directory except the read-only examples stated as audits, which are shown as commands to adapt, not transcripts.