I’m learning how to write shell scripts on my Mac OS X Mavericks system and would like to identify and extract specific information from the EXIF information in photos. Is that possible from the Bash shell command line?
Ah, what a refreshing question! I’ve been writing shell scripts for so long that it’s burned into my brain. Not to mention that I write a column on shell script programming for Linux Journal and am the author of the best selling Wicked Cool Shell Scripts for NoStarch Press. Yeah, I’ve been exploring shell scripts for a long time now, so I’m the perfect person to ask. 🙂
As you already know, photos taken by modern cameras (and cell phones) embed extra information including film speed, lens size, date, time, geographic location of the photo and many other items of data. EXIF, in fact, stands for “EXchangeable Image Format”, if you’re curious.
Generally it’s required special third party open source programs to access EXIF information on photos from the command line, but Mac OS X includes a slick utility called “mdls” (which stands for metadata-ls). Here’s what I see when I use “mdls” on a photo sitting on my desktop:
$ mdls IMG_1331.JPG kMDItemAcquisitionMake = "Apple" kMDItemAcquisitionModel = "iPhone 5s" kMDItemAltitude = 33.75805651958354 kMDItemAperture = 2.275007124536905 kMDItemBitsPerSample = 32 kMDItemColorSpace = "RGB" kMDItemContentCreationDate = 2013-12-19 15:19:49 +0000 kMDItemContentModificationDate = 2013-12-19 15:19:49 +0000 kMDItemContentType = "public.jpeg" kMDItemContentTypeTree = ( "public.jpeg", "public.image", "public.data", "public.item", "public.content" ) kMDItemCreator = "7.0.4" kMDItemDateAdded = 2013-12-30 06:19:43 +0000 kMDItemDisplayName = "IMG_1331.JPG" kMDItemEXIFVersion = "2.2.1" kMDItemExposureMode = 0 kMDItemExposureProgram = 2 kMDItemExposureTimeSeconds = 0.03333333333333333 kMDItemFlashOnOff = 0 kMDItemFNumber = 2.2 kMDItemFocalLength = 4.12 kMDItemFSContentChangeDate = 2013-12-19 15:19:49 +0000 kMDItemFSCreationDate = 2013-12-19 15:19:49 +0000 kMDItemFSCreatorCode = "" kMDItemFSFinderFlags = 0 kMDItemFSHasCustomIcon = (null) kMDItemFSInvisible = 0 kMDItemFSIsExtensionHidden = 0 kMDItemFSIsStationery = (null) kMDItemFSLabel = 0 kMDItemFSName = "IMG_1331.JPG" kMDItemFSNodeCount = (null) kMDItemFSOwnerGroupID = 20 kMDItemFSOwnerUserID = 501 kMDItemFSSize = 2163587 kMDItemFSTypeCode = "" kMDItemGPSDateStamp = "2013:12:19" kMDItemHasAlphaChannel = 0 kMDItemImageDirection = 310.9829545454546 kMDItemISOSpeed = 80 kMDItemKind = "JPEG document" kMDItemLatitude = 47.6087 kMDItemLogicalSize = 2163587 kMDItemLongitude = -122.3407216666667 kMDItemOrientation = 0 kMDItemPhysicalSize = 2166784 kMDItemPixelCount = 7990272 kMDItemPixelHeight = 2448 kMDItemPixelWidth = 3264 kMDItemProfileName = "sRGB IEC61966-2.1" kMDItemRedEyeOnOff = 0 kMDItemResolutionHeightDPI = 72 kMDItemResolutionWidthDPI = 72 kMDItemTimestamp = "16:19:48" kMDItemWhiteBalance = 0
As you can see, quite a huge amount of data is produced from the command, far more than you want, I’m betting!
To me, the most interesting parts are the camera info — camera name, focal length and ISO speed setting — the location of the photo — latitude / longitude — and the dimensions of the image.
For the first, look at kMDItemAcquisitionMake, kMDItemAcquisitionModel, kMDItemExposureTimeSeconds, kMDItemFNumber and kMDItemISOSpeed. The location is stored in kMDItemLatitude and kMDItemLongitude, and the dimensions are kMDItemPixelHeight and kMDItemPixelWidth.
From a scripting perspective, these are easily extracted and saved as named variables like this:
height=$(mdls IMG_1331.JPG | grep PixelHeight | awk '{print $3}')
Do that for the individual variables and you’ve got the values you want loaded up. Latitude and Longitude, for example? Like this:
lat=$(mdls IMG_1331.JPG | grep Latitude | awk '{print $3}') long=$(mdls IMG_1331.JPG | grep Longitude | awk '{print $3}') echo Photo was taken at $lat / $long
From this point I bet you can proceed with the script manipulation you seek!
In almost all cases, the use of grep and awk together is baffling. Awk has made grep obsolete pretty much as soon as it got released. If you use awk, there’s never a need to use grep.
grep foo | awk {bar}
can be replaced with
awk /foo/ {bar}
Another pet peeve of mine: using cat to feed something in a pipe. It’s not needed. Like ever. Say
cat foo | bar | baz
can be generically replaced with
bar < foo | baz
Ahh, always a purist in the group. 🙂 I am always seeking the balance between maximally efficient and maximally readable and understandable. So x < y | z might make sense to you, but for someone learning the shell that can be a bit baffling.
Cool, thanks. I am missing all of the lens info. Where is this hidden? Appreciate any help!
Not every camera or photo device records all the EXIF information. I think it’s all basically optional, so perhaps you’re missing the lens information because your camera isn’t recording it, Roland?
The kMDItemOrientation field is not to be confused with the EXIF standard orientation field (which contains a value from 1..9 if memory serves). kMDItemOrientation is 0 or 1 based on whether the image is in portrait or landscape format.
and forgotten…
mdls -raw -name kMDItemLatitude IMG_1331.JPG
will directly give the value, “47.6087” in your case.
instead of grep you can directly access the data with
mdls -name kMDItemLatitude IMG_1331.JPG