zero-width negative look-ahead assertion

Currently I need to do some bulk transformation of some file names.
I have, say, 3 file names: 2.640-849.0.jpg, g2650ohr.jpg, and k2.26mr.jpg (so these are treated as three separate strings)
I want a (hopefully) single regex to substitute all of the . (periods) with - (dashes) except for the .jpg extension.

A first draft would be

s/\./-/g

but that gives you 2-640-849-0-jpg. Note the extension is transformed as well.

I could then do a second regex like s/-jpg/.jpg/ but that feels less than optimal.

The solution is a zero-width negative look-ahead assertion
man perlre for more information on this, but the basic idea is that
you match occurances of the preceeding expression that ate not followed by the zwnla.
For example /foo(?!bar)/ matches any occurrence of foo that isn’t followed by bar.

Combine this with the common perl utility rename and we get the following:

rename -n 's/\.(?!jpg)/-/g' *

Note that Matz has decided not to include the zero-width negative look-ahead assertion to Ruby.

UPDATE
Thanks goes out to Matt Pulver for submitting these improvements to the above regex:

This will handles cases like test.jpg.jpg

s/\.+(?!jpg$)/-/g

If you want to substitute all non-alpha-numeric characters and support other file extensions try:

s/[^0-9a-z](?!\w+$)/-/g

Share:
  • del.icio.us
  • Reddit
  • Technorati
  • Twitter
  • Facebook
  • Google Bookmarks
  • HackerNews
  • PDF
  • RSS
This entry was posted in programming, sysadmin. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.