Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

Renaming Files with Perl

Rated 4.2 (Ratings: 7) (Add your rating)

Log in to add a comment
(28 comments so far)

Want more?

  • More articles in Code
  • More articles by dmah
 
Picture of dmah

Dean Mah

Member info | Full bio

User since: June 05, 1999

Last login: September 07, 2008

Articles written: 22

Have you ever wanted to rename more than one file at a time? Have you ever wanted to be able to rename a group of files based on a regular expression? Well the script below will let you do just that.

The Code

#!/usr/local/bin/perl
#
# Usage: rename perlexpr [files]

($regexp = shift @ARGV) || die "Usage:  rename perlexpr [filenames]\n";

if (!@ARGV) {
   @ARGV = <STDIN>;
   chomp(@ARGV);
}


foreach $_ (@ARGV) {
   $old_name = $_;
   eval $regexp;
   die $@ if $@;
   rename($old_name, $_) unless $old_name eq $_;
}

exit(0);

The Explanation

Save the above code into a file called rename. Make sure that the permissions are set correctly so that you can execute the script. Also check to make sure that your Perl interpreter is in /usr/local/bin. If Perl is somewhere else, you'll need to change the first line to point to where Perl is installed on your system.

To use the script you use:

rename perlexpr [files]

where perlexpr is the substitution operator, i.e., s///. You can actually pass any Perl expression through to perlexpr allowing you to do more complex file renaming actions. The files argument is a list of filenames that you want to change. You can leave the files argument out and the script will take a list of names from STDIN.

The Examples

  1. Make all the files in the directory end with .html instead of .txt.

    rename 's/txt$/html/' *
  2. Change all the files prefixed with the text mah and suffixed with .new to be suffixed with .old instead.

    rename 's/new$/old/' mah*.new
  3. Hide every file in the directory by prefixing the filename with a .

    rename 's/(.+)/\.$1/' *

The possibilities are endless. You should be careful however as you are dealing with regular expressions. You should be as specific as possible when specifying your patterns otherwise you may rename a file in a way that you had not anticipated.

For instance, take the first example. If you had typed:

rename 's/txt/html/' *

(notice the missing $ in the pattern?) and you had a file named newtxt.txt, the script would rename the file to newhtml.txt which might not have been what you wanted.

Hopefully this script will be useful to you. If you have any problems or questions, you can e-mail them to me at dean.mah@gmail.com.

Founding member of evolt.org.
claimID
*.e.o

Submitted by trayde on October 13, 1999 - 11:49.

Very nice indeed. Just what the doctor ordered! Thanks for this, saved me much time. J

login or register to post comments

Submitted by Anonymous on October 14, 1999 - 03:51.

Very nice indeed. Just what the doctor ordered! Thanks for this, saved me much time. J

login or register to post comments

Submitted by senorbim on November 29, 1999 - 19:42.

hmm. check out page 357 of the Perl Cookbook (O'Reilly). eerily similar. Larry Wall wrote the original apparently. In fact if you do anything in Perl and want to do it well, get this book - worth 10x its price in the long run.

login or register to post comments

Recursive Renaming

Submitted by dmah on July 24, 2003 - 10:13.

Want to rename files recursively? For example, you want to replace all space characters in file names and directory names with an underscore starting from a given directory. Here's an ugly-looking way that works in bash:

find "test dir" -print | tac | rename '($file) = (m|.+/(.+)|); $file = $_ if (!defined($file)); ($newfile = $file) =~ s/ /_/g; s/$file/$newfile/;'

Why the mess? You have to be careful not to change the name of the directories or the pathname while the script is working in that directory.

Let's say that you have a directory structure like:

test dir/
test dir/file one
test dir/sub dir/
test dir/sub dir/file one

If the script renames "test dir" to "test_dir" it won't be able to find the next file to rename, i.e., "test dir/file one" since "test dir" doesn't exist anymore, it's been renamed!

Since the script will run arbitrary Perl code, we can tell the script to only rename the leaf part of the pathname. Breaking the command down we have:

# Print all of the file names starting from "test dir".
find "test dir" -print |

# Print the list in reverse order as we want to rename the
# deepest levels first.
tac |

# Execute the rename script passing in the following Perl code.
rename

# Get the last element of the path which might be a filename.
'($file) = (m|.+/(.+)|);

# If we didn't get a filename, then this is a directory name
# and we can rename the original pathname since all of the
# files in the directory should have already been renamed.
$file = $_ if (!defined($file));

# Do whatever renaming we want (space to underscore in this example).
($newfile = $file) =~ s/ /_/g;

# Change the pathname so when the script does the renaming,
# it only changes the filename.
s/$file/$newfile/;'

login or register to post comments

Wildcards for filenames under Windows XP

Submitted by int3rn3tfr3ak on March 15, 2004 - 14:18.

I've been using Perl for about 2 months now. I'm running it on Windows XP Pro. But Windows doesn't seem to support wildcards in the filename(s) parameter... Is this right? And if so, is there a way to be able to use wildcards? I've already tried these out: *.* and *, but neither will work.

And I have another problem: what is the escape-character for a space? Cause if I try to just use a space, the script obviously won't work (because the parameters are processed with shift, right?), even when the x parameter is used.

For example, if I try to substitute "_" by a space, what should I use? I came up with
rename.pl s/(.*)_(.*)/$1 $2/gi<b>x</b> file_01_temp.html
but that won't work.

Please understand that I am still very new to Perl, so the solutions might be pretty obvious, and it turns out I just did'nt see them.

login or register to post comments

Re: Wildcards for filenames under Windows XP

Submitted by dmah on March 15, 2004 - 16:59.

The wildcard under Windows XP should be .* to match any character. Make sure that you are putting the regular expression in quotation marks otherwise the command line might eat up the * as a metacharacter, i.e, the shell parses and removes it before it is passed to Perl.

As for the space, you should be able to just use space like:

rename.pl "s/_/ /g;" file_01_temp.html

login or register to post comments

Windows Fix

Submitted by dmah on March 24, 2004 - 21:01.

I don't do a lot of Windows programming and so didn't realize until recently that when you try to pass a lot of files to the script with wildcards, it doesn't work. The command line passes the exact string to the Perl program. For instance, when using *.txt to rename all of the .txt files, the Perl script gets the string '*.txt' as a filename. Usually you aren't going to find a file with that name and so nothing happens.

To fix this, try adding

use File::Glob qw(:glob);

at line 4. And change the foreach loop to read:

foreach $_ (bsd_glob(@ARGV, GLOB_NOCASE)) {

login or register to post comments

Re: Windows Fix

Submitted by int3rn3tfr3ak on April 2, 2004 - 06:20.

Thanks alot Dean, the fix worked! Now I can finally try some of the regexe's I prepared to let loose on my website ( at least, the backup of course ... ).

login or register to post comments

how do I traverse subdirectories in Windows

Submitted by ross_m on May 18, 2004 - 13:51.

Dean, This is very nice script. How can I use it to traverse subdirectories in Windows? In UNIX/Linux I'd use find...

login or register to post comments

Re: how do I traverse subdirectories in Windows

Submitted by dmah on May 19, 2004 - 19:55.

Good question. I don't know if there is a command like 'find' for Windows. I guess that I'd write something in Perl to traverse the directories and print the filenames out and then pipe it into the rename script.

login or register to post comments

How to rename multiple file with same filename?

Submitted by happgirl on May 25, 2004 - 19:28.

Hi, May I know how to rename multiple file without changing the file extension. Like example: Rename all filename "abc" to "cdf". This will rename : (abc.txt -> cdf.txt) (abc.exe -> cdf.exe) (abc.o -> cdf.o) Any idea? Thanks

login or register to post comments

Re:How to rename multiple file with same filename?

Submitted by dmah on May 25, 2004 - 19:52.

If all of the files that you want to rename start with abc.[some extension], use:

rename.pl 's/abc/cdf/' abc.*

If you want to change any occurrence of 'abc' to 'cdf' anywhere in the filename, use:

rename.pl 's/abc/cdf/g' *

login or register to post comments

How about other way of renaming?

Submitted by happgirl on May 25, 2004 - 21:32.

Hi, thank you... rename 's/abc/cdf/' abc.* it works... How about if at the command prompt, i only allowed to type : rename abc cdf where abc filename to be change. cdf is new filename. So, what changes should make to the code? Thanks

login or register to post comments

How about other way of renaming?

Submitted by happgirl on May 25, 2004 - 21:35.

Hi, thank you... rename 's/abc/cdf/' abc.* it works... How about if at the command prompt, i only allowed to type : rename abc cdf where abc filename to be change. cdf is new filename. So, what changes should make to the code? Thanks

login or register to post comments

Re: How about other way of renaming?

Submitted by dmah on May 26, 2004 - 19:13.

Here's an example. It only checks files within the current directory.

#!/usr/local/bin/perl

die "Usage: rename from_regexp to_regexp\n" if (@ARGV != 2);

my ($from, $to) = @ARGV;

opendir(DIR, '.') || die "Couldn't opendir: $!\n";

foreach my $filename (grep !/^\.\.?/, readdir(DIR)) {
    my $new_name = $filename;
    $new_name =~ s/$from/$to/g;
    rename($filename, $new_name) unless $filename eq $new_name;
}

closedir(DIR);

login or register to post comments

HELP: error, can't get working... :S

Submitted by kevingpo on March 5, 2005 - 10:12.

Thanks. I keep on getting the error: $ ./rename : bad interpreter: No such file or directory No matter what input parameters I give it. I am using Cygwin on Windows XP. And I do have Perl installed Cygwin. Should I use ActivePerl instead? Can't see why Cygwin Perl shouldn't work :S

login or register to post comments

Re: HELP: error, can't get working... :S

Submitted by dmah on March 16, 2005 - 11:20.

Try running the script with 'perl rename' instead of using './rename'.

login or register to post comments

newbie

Submitted by damamajama on January 4, 2006 - 17:20.

new to perl, have a question.
i have files that i need to rename to include directories, i.e. i have many files such that they look like:
c:/xxx/yyy/zzz.ext
i need to rename them so they look like
c:/xxx/yyy/xxxyyyzzz.ext
any ideas? i have 151 folders with 2-5 subfolders each and about 34,000 files to rename, so a batch would be preferable
thanks!

login or register to post comments

Re: newbie

Submitted by dmah on January 6, 2006 - 00:48.

You can't do it with the script above but from a Unix command line you could run:
perl -MFile::Find -e '@dirs_to_search = "A"; find(\&rename, @dirs_to_search); sub rename { return if !-f; $suffix = join("", split("/", $File::Find::dir)); rename $_, "$suffix$_"; }'

login or register to post comments

Re: newbie

Submitted by damamajama on January 15, 2006 - 20:26.

worked like a charm - you rock! thanks!

login or register to post comments

rename by adding a suffix?

Submitted by roelru on February 6, 2006 - 20:17.

Your article was great. I've been looking for something similar for a while and this is the best example I've found so far. I do have one question: short of calling this script from a loop, is there a way to rename files by adding a letter such that the new filename will be unique?
the directory contains files: file1.xxx file1.yyy file1a.xxx file1a.yyy
I want to copy new files that would be named file1b.xxx file1b.yyy
Thanks.

login or register to post comments

Re: rename by adding a suffix?

Submitted by dmah on February 6, 2006 - 22:56.

Not 100% sure what you are looking for but I think that you'd have to have some kind of loop to keep the files names unique. If you can give a concrete example of a set of filenames and how they should be renamed, I can try to come up with something.

login or register to post comments

adding a zero to filename

Submitted by rikyo on June 20, 2006 - 01:13.

Good article. I can't work out how to make it (or whatever else might be more useful) for my situation.

I have a bunch of files: foo10sec foo20sec ... foo90sec foo100sec foo110sec foo120sec ... foo150sec

I want those with two-digit numbers chanaged to 3 digits ie foo10sec -> foo010sec and those already with 3 digit numbers left as they are.

I know unix rename can be made to do it, but only without suffixes as far as I can tell. Thoughts?

login or register to post comments

Re: adding a zero to filename

Submitted by dmah on June 25, 2006 - 14:33.

Try something like: rename '($num) = (/^foo(\d+)/); $_ = sprintf "foo%03dsec", $num;'

login or register to post comments

Read only Image files in a direcory

Submitted by inderpal on July 20, 2006 - 10:27.

Hi All,

Is there any way to find out praticular types of files in a directory using perl script?

Thanks in advance.

login or register to post comments

Re: Read only Image files in a direcory

Submitted by dmah on July 20, 2006 - 14:29.

Some ideas: 1) You could open up each file and read the first few bytes from it and then try to determine exactly what kind of file that you have. 2) Perl also has built-in operators to distinguish between text files and binary files. 3) Go based on the filename extension. You can run this script with *.gif as the file list, for example.

login or register to post comments

Re: Recursive Renaming

Submitted by pateretou on September 28, 2006 - 20:51.

Hello!
Great but I have a few issue:
it seems
($file) = (m|.+/(.+)|); $file = $_ if (!defined($file)); ($newfile = $file) =~ s/ /_/g; s/$file/$newfile/;

doesn't support filename with some symbol like "[", "]", "!" or "(" etc. (without double quote)
I'll try to find the problem but if you have any idea?

Thanks a lot!

http://www.crapules.com/wordpress
http://www.crapules.com/

login or register to post comments

Re: Recursive Renaming

Submitted by pateretou on September 30, 2006 - 10:36.

Hi!

A fix bug:
just add a quotemeta() in your bash script to escape $file

find "test dir" -print | tac | rename '($file) = (m|.+/(.+)|); $file = $_ if (!defined($file)); ($newfile = $file) =~ s/ /_/g;$file2=quotemeta($file); s/$file2/$newfile/;'

It's work better if you have some special caracters in your filename.

http://www.crapules.com/wordpress
http://www.crapules.com/

login or register to post comments

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.orgEvolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.