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.