The Awesome Unix Command Line

I recently posted some new photos ( and ), but had incorrectly copied the Superior ones out of the camera (I used cp -vi instead of cp -via which preserves dates. I wanted the correct date embedded in the filename, so I had to do a bit of file mangling to achieve that. The main tool I needed was jhead (part of a package of the same name in Debian), which can be used to "manipulate the non-image part of Exif compliant JPEG files." Basic jhead output looks like this (this is one of the corrected file):

$ jhead 20150911.3492.GO.CanonSX10.web.jpg
File name    : 20150911.3492.GO.CanonSX10.web.jpg
File size    : 259384 bytes
File date    : 2015:10:04 14:31:10
Camera make  : Canon
Camera model : Canon PowerShot SX10 IS
Date/Time    : 2015:09:11 12:40:51
Resolution   : 1000 x 750
Flash used   : No
Focal length :  5.0mm  (35mm equivalent: 29mm)
CCD width    : 6.12mm
Exposure time: 0.020 s  (1/50)
Aperture     : f/5.6
Focus dist.  : 0.28m
ISO equiv.   : 80
Whitebalance : Auto
Metering Mode: center weight
JPEG Quality : 75

The "File date" is the current date stamp, and not very useful: the thing we're looking for is "Date/Time", which is when the photo was taken (assuming you set the time correctly on your camera: I've made that mistake too).

To rebadge a bunch of files and their associated thumbnails, I did this:

$ for file in *.web.jpg; do newdate="$(jhead ${file} | grep "Date/Time" | awk ' { print $3 } ' | tr -d ":" )"; tmp=${file#*.} ; photobody=${tmp%.web.jpg} ; mv -vi ${file} ${newdate}.${file#*.} ; mv -vi ${file%.web.jpg}.thumb.jpg ${newdate}.${photobody}.thumb.jpg; done

This uses several Bash tricks:

  • standard Unix commands (mv -vi ...)
  • looping (for file in *.web.jpg; do ... done)
  • variable assignment (newdate=...)
  • parameter expansion (tmp=${file#*.} - here I'm saying "take the variable name "file" and strip off everything up to and including the first '.'")
  • command expansion ($(...) - "use the text generated by any command in these parentheses")
  • pipes (jhead ${file} | grep "Date/Time" which takes the output of jhead and prints only the line matching "Date/Time" using grep and feeds the output on to the next pipe command)

The biggest thing here is the creation of the "newdate" variable: newdate="$(jhead ${file} | grep "Date/Time" | awk ' { print $3 } ' | tr -d ":" )" . We take the jhead output, get the "Date/Time" line with grep, get the third field only with awk (in the example case above that would be "2015:09:11"), but I need it without the colons in it so I pipe it to tr, the ancient but still useful "translate" command, which deletes (-d) the colons.

To get the names of the thumbnail files I generate them from the image file: mv -vi ${file%.web.jpg}.thumb.jpg. ${file%.web.jpg} uses the "%" to say "strip everything off the end of "file" that matches what follows," and then I append ".thumb.jpg." If you're not familiar with this, I highly recommend you use man bash and then search for "Parameter Expansion": while it's not as powerful as some other text manipulation methods (sed and awk come to mind), it's built right into the shell and is faster.

Something I've found very useful when I'm assembling ugly commands like these at the command line is to slip in an echo command before any command that's going to make permanent changes, in this case, two instances of mv -vi .... With echo in place before them, it prints what it would do on screen and you can check that your text manipulations are working correctly. Finally, remove the "echo"s and run the final command.