Engineers love to change things. As I was writing this book, I found it almost irresistible to move and rename directories, variables, and shared modules in the book examples tree whenever I thought I'd stumbled onto a more coherent structure. That was fine early on, but as the tree became more intertwined, this became a maintenance nightmare. Things such as program directory paths and module names were hardcoded all over the place—in package import statements, program startup calls, text notes, configuration files, and more.
One way to repair these references, of course, is to edit every
file in the directory by hand, searching each for information that has
changed. That's so tedious as to be utterly impossible in this book's
examples tree, though; as I wrote these words, the examples tree
contained 118 directories and 1,342 files! (To count for yourself, run
1 in the PP3E examples root directory.)
Clearly, I needed a way to automate updates after changes.
There is a standard way to search files for strings on Unix
and Linux systems: the command-line program
grep and its relatives list all lines in
one or more files containing a string or string pattern.[†] Given that Unix shells expand (i.e., "glob") filename
patterns automatically, a command such as
grep popen *.py will search a single
directory's Python files for the string
"popen". Here's such a command in action on Windows ...