Finding Things
Table of Contents
- Two Kinds of Searching
- A Small Project to Search
- Finding Files by Name:
find - Finding Files by Type, Size, and Age
- Searching Inside Files:
grep -r - Narrowing a Search
- Where Is This Command?
whichandtype - Try It Yourself
1. Two Kinds of Searching
When you're looking for something on a computer, you're usually asking one of two questions:
- "Where is the file called…?" — you know (part of) the name,
but not where it is. That's a job for
find. - "Which file contains…?" — you know some text inside the file,
but not which file it's in. That's a job for
grep -r.
As a developer you'll ask the second question constantly: where is this function defined? Which files still use the old API URL? Where did that error message come from? Your editor has a search box for this, but on a server, in a container, or in a script, the terminal is all you have.
2. A Small Project to Search
In chapter 4 you created projects/website with src, tests, and
docs folders. Let's put some code in it. Run each block from inside
~/cli-practice:
cd ~/cli-practice
mkdir -p projects/website/{src,tests,docs}
printf '%s\n' \
"// Home page" \
"function showHome() {" \
" // TODO: add a welcome message" \
" return 'Welcome';" \
"}" > projects/website/src/home.js
printf '%s\n' \
"// Login page" \
"function login(user) {" \
" // todo: check the password" \
" return true;" \
"}" > projects/website/src/login.js
printf '%s\n' \
"test('home works', () => {});" \
"// TODO: test the login page" > projects/website/tests/home.test.js
printf '%s\n' \
"# Website" \
"Run npm install, then npm run dev." > projects/website/docs/README.md
You don't need to understand the JavaScript — it's just text to search
through. TODO comments are notes developers leave for work that still
needs doing, and finding them all is a very real task.
3. Finding Files by Name: find
find walks through a folder, and every folder inside it, and prints
every path that matches your conditions. The basic shape is:
find where-to-look conditions
Find every JavaScript file under the current folder:
find . -name "*.js"
./projects/website/tests/home.test.js
./projects/website/src/home.js
./projects/website/src/login.js
The . means "start here" (as you learned in chapter 3). You could
also start somewhere else, like find ~/Documents -name "*.pdf" or
find projects -name "*.md".
Notice the results aren't in alphabetical order — find prints them in
the order it comes across them on the disk. Pipe the output into sort
if order matters.
Always quote the pattern. Without quotes, the shell expands
*.js(chapter 4) beforefindever sees it. If the current folder happens to containapp.js, the command becomesfind . -name app.jsand silently finds only files with that exact name. With quotes, the*reachesfind, which matches it against every folder it visits.
-name is case-sensitive: -name "*.JS" won't match home.js. Use
-iname to ignore case:
find . -iname "readme*"
4. Finding Files by Type, Size, and Age
find can match much more than names. A few of the most useful
conditions:
| Condition | Matches |
|---|---|
-type f | Regular files only |
-type d | Directories (folders) only |
-size +100M | Files bigger than 100 MB (k, M, G work) |
-size -10k | Files smaller than 10 KB |
-mtime -7 | Modified in the last 7 days |
-mmin -30 | Modified in the last 30 minutes |
Conditions can be combined, and a file has to match all of them:
find . -type d # every folder
find projects -type f -name "*.js" # files ending in .js (not folders)
find . -type f -mmin -60 # files you changed in the last hour
That last one is surprisingly handy: "I just saved a file somewhere, but where?"
A real-world classic is hunting down what's eating your disk space:
find ~ -type f -size +500M 2> /dev/null
Searching your whole home folder hits some folders you're not allowed to
read, and each one prints a Permission denied error. 2> /dev/null
from the last chapter throws those errors away, so only the results are
left.
Finding and deleting
find has a -delete option that deletes every match. It's powerful
and dangerous — there's no confirmation and no trash. If you ever use
it, follow the same rule as with rm: run the exact command without
-delete first, read the list, and only then add -delete at the
end.
find . -name "*.tmp" # 1. look
find . -name "*.tmp" -delete # 2. only then delete
5. Searching Inside Files: grep -r
In chapter 6 you used grep on a single file. Add -r (recursive)
and give it a folder instead, and it searches every file inside:
grep -r "TODO" projects
projects/website/tests/home.test.js:// TODO: test the login page
projects/website/src/home.js: // TODO: add a welcome message
Each result shows the file it came from, a colon, and the
matching line. Add -n to see the line number too, so you can
jump straight to it in your editor:
grep -rn "TODO" projects
projects/website/tests/home.test.js:2:// TODO: test the login page
projects/website/src/home.js:3: // TODO: add a welcome message
Notice that login.js is missing — its comment says todo in
lowercase. Add -i to ignore case:
grep -rni "todo" projects
Now all three show up. -rni is worth memorizing: recursive, line
numbers, ignore case — it's the combination you'll use most.
If you only want to know which files match, not every line, use
-l:
grep -rli "todo" projects
projects/website/tests/home.test.js
projects/website/src/home.js
projects/website/src/login.js
And -w matches whole words only, so searching for log with -w
won't match login:
grep -rnw "login" projects
6. Narrowing a Search
In a real project, a plain grep -r quickly drowns you in noise. A
Node.js project's node_modules folder can easily contain tens of
thousands of files, and Git keeps its own history in .git. You almost
never want to search those.
Two options keep searches focused:
grep -rn "TODO" --include="*.test.js" projects # only test files
grep -rn "TODO" --exclude-dir=node_modules . # skip node_modules
You can repeat --exclude-dir for several folders:
grep -rn "apiUrl" --exclude-dir=node_modules --exclude-dir=.git .
And of course, grep results can go into a pipe like anything else:
grep -rni "todo" projects | wc -l # how many TODOs are left?
A faster alternative: many developers install ripgrep (command name
rg), a modern replacement forgrep -r. It's much faster on big projects and skipsnode_modules,.git, and anything in.gitignoreautomatically. Everything you learn aboutgrepcarries straight over. VS Code's own search box actually uses ripgrep under the hood.
7. Where Is This Command? which and type
Commands are files too. When you type ls or git, the shell runs a
program file stored somewhere on the disk. To find out where, use
which:
which git
/usr/bin/git
This becomes important the moment something odd happens: you installed
a new version of Node.js, but node --version still shows the old one.
which node tells you which copy of node the shell is actually
running — and very often, it's not the one you expected.
Add -a to see every copy the shell can find, in the order it
checks them:
which -a python3
Some commands aren't program files at all. cd, for example, is
built into the shell itself — it has to be, because a separate
program can't change the shell's own current directory. type tells
you exactly what a name is:
type cd
type ls
type git
cd is a shell builtin
ls is an alias for ls -G
git is /usr/bin/git
That middle line may surprise you. On many systems, ls is an
alias — a shortcut the shell expands into a longer command, here
ls with an option that adds colours (on Ubuntu you'll see
ls --color=auto instead). If yours just says ls is /bin/ls, there's
no alias set up. You'll make aliases of your own in chapter 9.
If which prints nothing, or type says not found, the shell can't
find that command at all — which is exactly what command not found
means. How the shell decides where to look is the subject of chapter
9, on PATH.
8. Try It Yourself
Do all of this inside ~/cli-practice.
- Use
findto list every.txtfile incli-practice, then pipe the result intosortandwc -lto count them. - Find every folder (not file) inside
projects. - Find all the files you've modified in the last 30 minutes.
- Run
find ~ -type f -size +100M 2> /dev/null. Do you recognize the large files it finds? - Use
grep -rnito find every TODO inprojects. Then use-lto show only the file names, and count them withwc -l. - Search for the word
functioninprojects, but only in files undersrc. (Hint: you can givegrepa more specific starting folder.) - Run
whichandtypeonls,cd,git, andprintf. Which ones are programs on disk, and which are built into the shell?
You can now find any file by name and any line of text across a whole project. Next, let's look at the programs you run as processes: how to see them, stop them, and run them in the background.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!