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?
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?
Best answer: Oh, and you can do
posted by Salvor Hardin at 1:11 PM on December 12, 2010
rename -n 's/\ /\_/g' *.jpgto see the change without actually executing it.
posted by Salvor Hardin at 1:11 PM on December 12, 2010
Response by poster: That did it! Thanks very much!
posted by Asparagirl at 1:23 PM on December 12, 2010
posted by Asparagirl at 1:23 PM on December 12, 2010
Best answer: If `rename` isn't present on the system (it's a perl script), here's a bash alternative.
posted by koudelka at 1:27 PM on December 12, 2010
for i in *.jpg; do mv "$i" "`echo $i | sed 's/ /_/'`"; done
posted by koudelka at 1:27 PM on December 12, 2010
Best answer: Or, to do it all in bash without calling out to sed:
Or if you'd like to replace all spaces with _s, not just the first one:
posted by JiBB at 2:31 PM on December 12, 2010 [1 favorite]
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 [1 favorite]
Best answer: 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:
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,
posted by Malor at 12:23 AM on December 13, 2010 [1 favorite]
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
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
posted by JiBB at 3:15 PM on January 13, 2011
This thread is closed to new comments.
posted by Salvor Hardin at 1:10 PM on December 12, 2010