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; the examples of the prior edition contained 186 directories and 1,429 files! Clearly, I needed a way to automate updates after changes. There are a variety of solutions to such goals—from shell commands, to find operations, to custom tree walkers, to general-purpose frameworks. In this and the next section, we’ll explore each option in turn, just as I did while refining solutions to this real-world dilemma.
If you work on Unix-like systems, you probably already know
that there is a standard way to search files for strings on such
platforms—the command-line program
grep and its relatives list all lines in one or more files
containing a string or string pattern. Given that shells expand ...