Join 3,376 readers in helping fund MetaFilter (Hide)


Renaming a whole bunch of files at once, in bash
December 12, 2010 1:08 PM   Subscribe

Please help me rename ~1300 image files in bash, en masse.

I have a long list of files that are named like this:

buczacz_(186-1-5723 a.)_0001_(IMG_6459).jpg
buczacz_(186-1-5723 a.)_0002_(IMG_6461).jpg
buczacz_(186-1-5723 a.)_0003_(IMG_6462).jpg
etc.

As you can see, there is a space between the "5723" and the "a." in those names. I would like to replace that space with an underscore character so that the filenames end up like this:

buczacz_(186-1-5723_a.)_0001_(IMG_6459).jpg
buczacz_(186-1-5723_a.)_0002_(IMG_6461).jpg
buczacz_(186-1-5723_a.)_0003_(IMG_6462).jpg
etc.

This needs to be done in bash on a shared web hosting environment, so I can't install any fancy tools to assist me. I'm sure there is a simple regular expression to handle something like this, but I don't know how to construct it. My geek-fu is not strong enough. Please help?
posted by Asparagirl to Computers & Internet (8 answers total) 8 users marked this as a favorite
 
I think this should do it, assuming you want to do this with all the files in whatever directory you're working in:
rename 's/\ /\_/g' *.jpg

posted by Salvor Hardin at 1:10 PM on December 12, 2010


Oh, and you can do
rename -n 's/\ /\_/g' *.jpg
to see the change without actually executing it.
posted by Salvor Hardin at 1:11 PM on December 12, 2010


That did it! Thanks very much!
posted by Asparagirl at 1:23 PM on December 12, 2010


If `rename` isn't present on the system (it's a perl script), here's a bash alternative.
for i in *.jpg; do mv "$i" "`echo $i | sed 's/ /_/'`"; done

posted by koudelka at 1:27 PM on December 12, 2010


Or, to do it all in bash without calling out to sed:
for f in *.jpg; do mv "$f" "${f/ /_}"; done

Or if you'd like to replace all spaces with _s, not just the first one:
for f in *.jpg; do mv "$f" "${f// /_}"; done
posted by JiBB at 2:31 PM on December 12, 2010 [2 favorites]


For posterity, those last two answers probably won't work correctly. bash, by default, breaks on spaces. On each pass through the for loop, the filename variable won't get the whole filename. Rather, it will get everything up to the space on the first pass, everything after the space on the second pass, the first half of the second filename on the third, and so on through the list of files. The loop will execute twice as many times as it should, never getting the correct filenames. When doing a mass rename, this can muck things up something fierce.

To get around that, you need to set the IFS variable to just newline, rather than the normal settings. This is how I did it in my most recent script that needed it:

OIFS=$IFS
newline='
'
IFS=$newline

(code body goes here)

IFS=$OIFS


I don't remember why I used the newline variable to set the value indirectly. Note that you need to hit enter immediately after the ' on the newline= command, and then type the other '. This will only work properly if you're using a Unix-style editor that embeds just a newline... if you're using a DOS-style editor, it will insert a carriage return/newline combo, and the script will break. (I think you'll get the entire list of files in one big variable.)

In theory, IFS=$'\n' is supposed to work, but the last time I was working with IFS, that version broke on me for reasons I never determined. Looking at it with fresh eyes, maybe I confused my ' and " characters, which have different meanings in bash. Regardless, going to the quote trick straightened me out.
posted by Malor at 12:23 AM on December 13, 2010 [1 favorite]


Oh, it would also work fine if you were to just type it in at the command prompt. I was assuming that you'd be writing a script in an editor, but you certainly could just type those commands in manually if you wished.

Messing with IFS can give you highly unexpected results in routine use if you forget to change it back, so limiting its use to scripts isn't a bad idea.
posted by Malor at 12:30 AM on December 13, 2010


Hmm... I'm not the most expert shell scripter, but in a quick test (Bash 3.2.48, Mac OS 10.6), my answer seems to work both at the command prompt, and in a script. $IFS is set to a space, a tab, and a newline. But, I suppose it doesn't hurt to be safe. (It also doesn't hurt to test batch renaming scripts before running them on non–backed-up data.)
posted by JiBB at 3:15 PM on January 13, 2011


« Older He's been in our room in a bas...   |  My 2005 VW Golf 1.9 litre TDI ... Newer »
This thread is closed to new comments.