Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

One-line batch file renamer UNIX
This one was posted to the X4U mailing list by Dierdre M., and I think it's an incredibly useful tip, so I'm posting it here!

If you want to batch rename a bunch of files (say "foo*.jpg" to "bar*.jpg"), you might think you could just do "mv foo*.jpg bar*.jpg" in the Terminal. However, this doesn't work right since the shell expands each argument before the execution occurs. However, there's a cool way to accomplish the same result with a (more complex) command line argument.

Open a terminal, and "cd" your way to the directory of interest (or just drag the folder you want to work with onto the terminal icon in the dock; it will open in that directory). Once there, we'll run a 'test' before actually change any names. This first version of the command is "proof of concept"; it will output what will happen, without actually doing it. So to rename all those "foo*.jpg" files into "bar*.jpg" files, type:
ls foo*.jpg | awk '{print("mv "$1" "$1)}' | sed 's/foo/bar/2'
This should output a series of "mv" (the unix "move" command, which is used to rename files) lines, each one showing the old and new name for each file affected. If it all looks right, then just pipe the output to the shell to execute:
ls foo*.jpg | awk '{print("mv "$1" "$1)}' | sed 's/foo/bar/2' | /bin/sh
That should do the trick. Dierdre points out that this is an especially nice way to do it, since you get to see what will happen before you commit to it! I happen to agree with that logic completely!

To use this on your own files, you'll need to replace the references to filenames and items to be replaced in both the "ls" and "sed" portions of the script.
    •    
  • Currently 4.38 / 5
  You rated: 5 / 5 (13 votes cast)
 
[95,173 views]  

One-line batch file renamer | 21 comments | Create New Account
Click here to return to the 'One-line batch file renamer' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
use perl;
Authored by: babbage on May 09, '01 03:21:16PM
Care for a perl solution? Save the following as something like ~/bin/rename:
#!/usr/bin/perl -w
# rename - larry wall's filename fixer

$op = shift or die "Usage: rename expr [files]n";
chomp (@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
   $was = $_;
   eval $op;
   die $@ if $@;
   rename ($was,$_) unless $was eq $_;
}

Now if you're comfortable with regular expression syntax (and hey, who isn't? ;), all you have to type is the following:

rename 's#.jpg#.jpg.bak#' *jpg

Note that this command doesn't come with an "undo" button, and can make a mess if you're not careful. As I am often not. :) If you *are* careful however, this can be a nice shortcut...

[ Reply to This | # ]

use perl;
Authored by: robh on May 10, '01 05:05:11AM

I wrote a similar Perl script for myself. Unlike Larry's, mine shows a list of changes that are to be made and prompts the users to confirm it's okay to proceed. When it comes to renaming files with Perl expressions, it's vital to see if your expression does what you think it does before it does it :-) (I often have to adjust the expression until I get it just right).

Here's the script... #!/usr/bin/perl $expr = $ARGV[0]; @files = @ARGV[1..$#ARGV]; foreach (@files) {  $old = $_;  eval($expr);  next if $_ eq $old;  print "$old ==> $_n";  $RENAME{$old} = $_; } $|=1; print "Rename these ? "; if ( <STDIN> =~ /y/i) {  foreach $f (keys %RENAME) {   print STDERR "mv $f $RENAME{$f}n";  print STDERR `mv '$f' '$RENAME{$f}'`;  } } else {  print STDERR "Nothing renamedn"; } I also named the script "rename" and run it like this example: rename 's/.jpg/.jpeg/' *.jpg The output showing what will happen looks like this example: Fire_and_deer.jpg ==> Fire_and_deer.jpeg crash.jpg ==> crash.jpeg floods.jpg ==> floods.jpeg myrrh.jpg ==> myrrh.jpeg Rename these ?

[ Reply to This | # ]
Learning Perl
Authored by: Anonymous on May 10, '01 06:39:11AM

Of course the point of the advanced rename (thank you) is that it lets you check before committing, but just to be perlsnickety, with 's/.jpeg/.jpg/' you would get:

hajjpeg ==> hajjpg

(because the . matches anything; use a backslash before the . to match just a period.)



[ Reply to This | # ]
Learning Perl
Authored by: robh on May 10, '01 07:09:55AM

You're right. There are various workarounds, if I was worried about mismatches I'd use 's/\.jpg$/.jpeg/'

I avoided the '\'; in my earlier example because the forum software here keeps eating escape characters after doing a preview of the comment. I hope they're intact here :-)



[ Reply to This | # ]
use perl;
Authored by: Anonymous on May 14, '01 04:58:47PM

this doesn't seem to work right on files with spaces in their names, whereas Larry's did.



[ Reply to This | # ]
use perl; correction
Authored by: Anonymous on May 14, '01 05:01:04PM

sorry, it turns out only the feedback message is broken for files with spaces in their names.
the script renames them just fine.



[ Reply to This | # ]
use a GUI file renamer?
Authored by: osxpounder on Mar 20, '03 04:47:11PM

I apologize in advance if I misunderstood the point, but it seems to me that if ease and simplicity are valuable to you, you might want to try FileRenamer.

I checked MacUpdate a minute ago, and it was still available. Apparently the authors' site is down, and it seems not to have been finished, but it works... and I use it. It lets you select a folder with files to be renamed [and that's 1 difference I see now between this and your CLI suggestions], offers some useful options such as appending or prepending sequential numbers, and it previews changes before committing. I use it to rename 100s of TGA files rendered from a 3D app so that they will sequence properly when I load them into QuickTime.

Hope this helps,

OSXPOUNDER

---
--
osxpounder



[ Reply to This | # ]
csh version
Authored by: sven on May 10, '01 04:56:06AM

Ok, let's start the big "Here's my way" contest. Let me submit the csh version with "foreach":

[Galileo:~/Temp] sas% touch 1.jpg
[Galileo:~/Temp] sas% touch 2.jpg
[Galileo:~/Temp] sas% touch 3.jpg
[Galileo:~/Temp] sas% foreach f ( *.jpg )
foreach -> set base = `basename $f .jpg`
foreach -> mv $base.jpg $base.gif
foreach -> end
[Galileo:~/Temp] sas% dir *.gif *.jpg
-rw-r--r-- 1 sas staff 0 May 10 10:14 1.gif
-rw-r--r-- 1 sas staff 0 May 10 10:14 2.gif
-rw-r--r-- 1 sas staff 0 May 10 10:14 3.gif

Note that the "foreach ->" prefix is printed by the shell after you hit return at the end of the "foreach" line. You get out of this with "end" on a line by itself. That's when the loop is actually executed.

I think this approach is a little more transparent than the awk/sed thing, especially since according to Murphy you'll never remember the exact syntax when you need it. And of course you won't have the time to read the man pages or they won't be installed or ... well, you know, that kind of thing...
foreach is really great when you don't have to modify the items like in the example above, for example if you want to batch run a program that doesn't take multiple files as input. E.g.:
[Galileo:~/Temp] sas% foreach f ( /Users/sas/some_files* )
foreach -> some_prog $f
foreach -> end

The drawback is that you can't use the history to get back what you typed inside the loop.

Sven



[ Reply to This | # ]
What about filenames with spaces?
Authored by: Chas on May 11, '01 10:44:37PM

This trick sounds good, but it doesn't work with files with spaces in the names. On Unix, I'm used to manipulating files with names like "really_long_filename.txt" but on the Mac they tend to be things like "really long filename.txt" and they don't parse the same way, you need extra help in deglobbing. I haven't quite figured this out, any hints or tricks you could suggest? I just had to manually rename 40 files because of this problem.



[ Reply to This | # ]
What about filenames with spaces?
Authored by: elfkinz on May 20, '02 12:31:54PM

Try adding sed 's/ / /g' after the ls and before the awk:

ls foo*.jpg | sed 's/ / /g' | awk '{print("mv "$1" "$1)}' | sed 's/foo/bar/2'

This should take care of the spaces ... the only problem I can't figure out yet is removing spaces from the file names.



[ Reply to This | # ]
What about filenames with spaces?
Authored by: xurizaemon on Jul 17, '08 03:35:36PM

The perl script "rename" above handles them fine.

I love stringing various tools together, but that little perl snippet does what I mostly need (piping the filenames thru sed) without me having to think twice about quoting.



[ Reply to This | # ]
Hypertalk versus unix..
Authored by: RagnarokOfBorg on Jan 19, '11 08:25:12PM

AAARGGH! (Now I understand how my mother feels about the Finder - how I feel about perl and unix..) I try that and it complains about an invalid command code ? (sed: 1: "s/Voyager/VOY/2": invalid command code ?). The perl script complains that it can't understand $expr.. I'll just fire up Revolution a bit later and write it in damn (nouveau) HyperTalk.. At least it has a command " replace "foo" with "bar" in tStr ".. Now I'll have to see if it has a command 'rename file <>' Oh, I'll just use the " shell() " function - which I found after about a second with google.. Apple really dropped the ball when they killed Hypercard - and then the apocryphal QuickTime 3..



[ Reply to This | # ]
Oops, I goofed on that one ...
Authored by: elfkinz on May 20, '02 12:37:00PM

Somehow I lost a few \s on the last posting ... What I meant to say was:

Try adding sed 's/\ /\\\ /g' after the ls and before the awk:

ls foo*.jpg | sed 's/\ /\\\ /g' | awk '{print("mv "$1" "$1)}' | sed 's/foo/bar/2'

This should take care of the spaces ... the only problem I can't figure out yet is removing spaces from the file names.



[ Reply to This | # ]
removing/replacing spaces in filenames.
Authored by: jhary on Mar 20, '03 12:16:01PM

just a one liner but say you have a bunch of mp3 files with spaces in their names and you wanted to replace the spaces with _ you can do
for file in *.mp3 ;do mv $file `echo $file | perl -p -e "s/\s+/_/g"` ;done

(this actualy replaces any number of spaces with one _ )
no backups though so make one before you try if youre worried. oh and it adds a _ at the end but thats easily fixed by doing
mv *.mp3_ *.mp3



[ Reply to This | # ]
use ruby
Authored by: ngb on Jul 14, '04 06:01:28PM

alias rubyrename='ruby -e "a = ARGV.shift; b = ARGV.shift; ARGV.each{ |f| File.rename(f, f.gsub(a, b)) }"'
use as follows

rubyrename oldstring newstring filelist
alternatively, you could just hardcode values for a and b

[ Reply to This | # ]
One-line batch file renamer
Authored by: loubregand on Jul 28, '04 07:14:29AM

This is my version of the script of Dierdre, which has been improved to handle files with spaces in them and recurse into the subdirectories (find is a very flexible tool for this kind of things):
[code]
find . -iname "*pattern1*" -exec echo "mv '{}' '{}'" \; | sed 's/pattern2/pattern3/2' | /bin/sh
[/code]
where:
pattern1: the mask that select which files to process
pattern2: string to be modified
pattern3: substitution for pattern2 (leave empty for deletion)

Note that, if the script has modified the folder names, you will have to replay the script for every level of recursion, since once it has changed the first level name it will no more find the nested folders.



[ Reply to This | # ]
One-line batch file renamer
Authored by: carmatana on Jan 04, '11 11:35:26PM

Hi loubregand,

Your lines have been the more helpful of all that I tried. I have one question: What if I want to make the rename (sed) just on files, excluding folders or directories at all. I have tried:

find . -type f -iname "*pattern1*" -exec echo "mv '{}' '{}'" \; | sed 's/pattern2/pattern3/2' | /bin/sh

and several more permutations but with no success

something that works is to use for pattern1 somethin like "*pattern1*.*"
and this works as long as the file has an extension, if it does not have one it is not processed

Could you or someone else can help me?



[ Reply to This | # ]
One-line batch file renamer
Authored by: davehay on Jan 26, '10 12:18:16PM

Rob, thanks for this hint, much appreciated - came in great use for a piece of work that I'm doing with VMware, as per my blog here: -

http://www.davehay.f2s.com/2010/01/moving-and-renaming-vmware-images-wip.html

Again, thanks



[ Reply to This | # ]
One-line batch file renamer
Authored by: blueshifter on Feb 26, '10 04:43:02AM
here's my version, the OP didn't allow me to append a file type to the original file, which were of form "foonnn", where nnn represent the numbers 1 through 500.

test version:
ls | egrep "foo[0-9]*" | while read filename; do echo "mv $filename $filename.jpg" ; done

doit version:
ls | egrep "foo[0-9]*" | while read filename; do mv $filename $filename.jpg ; done

[ Reply to This | # ]

One-line batch file renamer
Authored by: carmatana on Jan 05, '11 05:22:39AM

Hi loubregand,

Your lines have been the more helpful of all that I tried. I have one question: What if I want to make the rename (sed) just on files, excluding folders or directories at all. I have tried:

find . -type f -iname "*pattern1*" -exec echo "mv '{}' '{}'" \; | sed 's/pattern2/pattern3/2' | /bin/sh

and several more permutations but with no success

something that works is to use for pattern1 somethin like "*pattern1*.*"
and this works as long as the file has an extension (and if directories do not have dots in their names), if it does not have one it is not processed

Could you or someone else can help me?



[ Reply to This | # ]
One-line batch file renamer
Authored by: Neville Hillyer on May 11, '11 12:32:44PM

I have very limited experience in this area but was surprised by carmatana's remark so I did a few tests.

My tests indicate that '-type f' restricts 'find' to files (ie not directories) as the manual says.

Edited on May 11, '11 12:40:37PM by Neville Hillyer



[ Reply to This | # ]