Finding Files

and things of that nature.

So, you’re loving life, using ls(1) to list your files, and making directories with mkdir(1), impressing people by printing out their names using banner(1) , and jumping around the command line like a pro when someone asks you if you still have a copy of that photo you emailed them last year with all your old high school friends in it. Maybe? If you’re anything like me, your home directory is a bit of a mess, with folders and files everywhere. In the DOS days, you’d do something like:

dir *.jpg to find all files that end with the .jpg extension, so you give that a try:

$ ls *.jpg
profile.jpg         profile-small.jpg

Which shows you a few files that seem to match your query, but they are both in the current folder, and you don’t want to have to cd into every folder and run the same command. There must be a better way, you think to yourself, and indeed, there is.

Introducing find(1)

find(1) is one of those incredibly powerful and intimidating unix tools. It can do all kinds of things, and looking through the man page for it, or seeing an example of using it on StackExchange can send you right back to your GUI. But, don’t distress, with a bit of time you too can find things like we had to before iPhoto was a thing.

In order to perform the same search for *.jpg files from the current directory and all it’s sub directories we could do:

$ find . -name "*.jpg"
./Sync/InstantUpload/IMG_20140701_065031.jpg
./Sync/InstantUpload/IMG_20140625_143923.jpg
./Sync/InstantUpload/IMG_20140701_065042.jpg
./Sync/InstantUpload/IMG_20140913_123816.jpg
./Sync/InstantUpload/IMG_20140828_060833.jpg
./Sync/InstantUpload/IMG_20140816_153202.jpg
./Sync/InstantUpload/IMG_20140823_145243.jpg
./Sync/InstantUpload/IMG_20140914_120422.jpg
./Sync/InstantUpload/IMG_20140914_120415.jpg
./Sync/InstantUpload/IMG_20140914_120417.jpg
./Sync/DSC_0237.jpg
...

A break down of that command is:

  • find: run the find command
  • .: From the . or current directory. You can specify any path here if you like, for example to search from your home directory ~/ or from the root directory of the system /.
  • -name: Match the pattern that follows against the file name (or directory name)
  • "*.jpg": The pattern we’d like to match against. Anything (*) followed by the .jpg extension.

Narrowing The Search

Running that from my home directory returns pages of output, all the .jpg files that I have. There are way too many for me to sort through on my own, so instead we’ll try to narrow our search. Let’s use the -type flag to the find(1) command to limit our search to only files of type “file” and ignore things like directories, special files, symbolic links, and sockets.

We also know that the file we’re looking for is no more than a year old, so we’ll tell find(1) to show us files newer than 1 year:

$ find . -name "*.jpg" -type f -mtime -365

We’ve added the -type f flag which tells find(1) that we only care about regular files, and we’ve added the -mtime -365 flag which says show only files that have a modification time less than 365 days ago (less than one year old).

We can also look for files that are at least 360 days old, to narrow our search to five days last February:

$ find . -name "*.jpg" -type f -mtime -365 -mtime +360
./Calibre Library/Michael De La Maz/Why Agile Works (60)/cover.jpg
./GoBlog/img/anne.jpg
./GoBlog/img/faraz.jpg
./GoBlog/img/zain.jpg
./Library/Mail/V4/46BA7B01-7738-4D0B-B4CA-9AEF087FE429/[Gmail].mbox/All
Mail.mbox/BB3A165D-460C-4016-9A68-25A9D4B1733F/Data/0/9/Attachments/90765/2/51867771.jpg
./Library/Mail/V4/46BA7B01-7738-4D0B-B4CA-9AEF087FE429/[Gmail].mbox/All
Mail.mbox/BB3A165D-460C-4016-9A68-25A9D4B1733F/Data/0/9/Attachments/90766/2/51867771.jpg

Neat, we’re down to only 6 files, I can easily check those by hand, and if the photo isn’t one of them, I can tell my friend that I looked, but I couldn’t find the file.

how to view image files from the cli is left as an exercise to the reader :)

The -mtime (and -atime, -Btime, -ctime) directive takes either a plus (more than) or minus (less than) sign followed by a number indicating days. You may also specify (s)econds, (m)inutes, (h)ours, (d)ays, or (w)eeks, using those letters after your number. For example, to find .jpg files that were modified last week:

$ find . -name "*.jpg" -type f -mtime -7d

or

$ find . -name "*.jpg" -type f -mtime -1w

Doing more with find(1)

Another thing you can do with find(1) is pass it’s output to another program for further work, so for example if you wanted to confirm your date range was valid, you could pass the files that are found to the ls(1) command with the -l flag set, to get the long listing:

$ find . -name "*.jpg" -type f -mtime -365 -mtime +360 -exec ls -l {} \;
-rw-r--r--  1 gabe  staff  407515 23 Feb  2016 ./Calibre Library/Michael De La
Maz/Why Agile Works (60)/cover.jpg
-rw-r--r--  1 gabe  staff  11161 23 Feb  2016 ./GoBlog/img/anne.jpg
-rw-r--r--  1 gabe  staff  73366 23 Feb  2016 ./GoBlog/img/faraz.jpg
-rw-r--r--  1 gabe  staff  4614 23 Feb  2016 ./GoBlog/img/zain.jpg
-rw-r--r--@ 1 gabe  staff  90427 24 Feb  2016
./Library/Mail/V4/46BA7B01-7738-4D0B-B4CA-9AEF087FE429/[Gmail].mbox/AllMail.mbox/BB3A165D-460C-4016-9A68-25A9D4B1733F/Data/0/9/Attachments/90765/2/51867771.jpg
-rw-r--r--@ 1 gabe  staff  90427 24 Feb  2016
./Library/Mail/V4/46BA7B01-7738-4D0B-B4CA-9AEF087FE429/[Gmail].mbox/AllMail.mbox/BB3A165D-460C-4016-9A68-25A9D4B1733F/Data/0/9/Attachments/90766/2/51867771.jpg

In the output I can confirm that the files that were found are all within 5 days of Feb 21st, one year ago. I can use that output to tweak my -mtime parameters to zero in on the exact date range I’m looking for.

The -exec option, allows us to specify another program to run: ls -l. Those squiggly braces {} tell find that we want to inject the path name of the found file at that point in the input to the new command. The \; tells find that this is the end of our second command. find(1) only cares that there is a ; (semicolon), but since ; has special meaning in the shell, it needs to be escaped with a \ first. Also, that space is important!

As always, if you want more information on the find command, you can just type man find at your prompt and read away.


Have a suggestion for a future post? A question about an article? Just want to say hi? Drop me an email: gabe@cotcli.com

Want to receive these posts directly to your inbox? Subscribe here.