grep1-2 Exercise 1: Python and JavaScript Files with TODO, Without the Noise ============================================================================= (Run from an empty folder. The first block builds the practice project from the chapter; your own tree can skip it.) mkdir -p project/src project/notes project/build project/logs project/data project/.git "project/my notes" cd project printf '# Demo project\nTODO write the install guide\nRun app.py to start\n' > README.md printf 'import os\n# TODO handle errors\ndef main():\n print("hello")\n' > app.py printf 'def helper():\n # TODO remove this\n return 1\n' > src/util.py printf '// TODO rename\nconsole.log("hi");\n' > src/main.js printf 'buy milk\nTODO call the plumber\n' > notes/todo.txt printf '/* generated */ var a = 1; // TODO generated noise\n' > build/bundle.js printf 'INFO started\nERROR something broke\n' > logs/app.log printf 'data\0TODO inside a binary file\n' > data/image.bin printf 'API_KEY=abc123\npassword=hunter2\n' > .env printf '[core]\n\trepositoryformatversion = 0\n# TODO placeholder inside .git\n' > .git/config printf 'TODO book the room\npassword reminder: change it\n' > "my notes/meeting notes.txt" # --- the exercise --- grep -rlI TODO --include='*.py' --include='*.js' --exclude-dir=.git --exclude-dir=build . | sort Output: ./app.py ./src/main.js ./src/util.py WHY THIS WORKS AS AN ANSWER --------------------------- -r walks the tree and -l prints each matching file once. --include twice allows both kinds of file (a name that matches either is searched). --exclude-dir twice removes build/ and .git/, so the generated build/bundle.js is gone even though its name ends in .js. -I is not needed to hide data/image.bin here (it is not a .py or .js file, so --include already excludes it) but it is the right habit for source trees. Without -I and without --include, the binary would be listed. sort is last because grep lists files in folder order, which is not alphabetical and differs between file systems; with sort the answer is the same everywhere.