Updated:
We're under the assumption that "clip-2009-10-01 21;26;00.mov" is not the actual filename; one possibility is that the actual filename is "clip-2009-10-01 21:26:00.mov". However, we can't verify that under Windows.
We may not need to.
Failsafe Method:
Boot to a Linux LiveCD. Ubuntu 9.04 has good NTFS support, and Linux handles a lot more wonky-characters-in-filenames than Windows. The perl rename script may be included as the system's rename command.
This-Might-Actually-Work Batch Method (New Script!)
The DOS command DIR/X shows short filenames, if they exist on your system.
$ cmd
c:\test> dir /x
Volume in drive E is NUVOL
Volume Serial Number is 80D3-A96D
Directory of e:\tor\test
10/04/2009 05:15 AM <DIR> .
10/04/2009 05:15 AM <DIR> ..
10/04/2009 05:11 AM 0 CLIP-2~1.MOV clip-2009-10-01 21;26;00.mov
1 File(s) 0 bytes
2 Dir(s) 5,201,670,144 bytes free
If they do exist, the REN command will move them to a new name; the new name can be a new (valid) long filename.
c:\test> ren CLIP-2~1.MOV "clip-2009-10-01_21-26-00.mov"
That's how to fix one.
To batch process all of them, you need to 1) grab a listing of all the files you want to move; 2) run a short perl script to convert your listing into a batch file with the appropriate REN commands; and 3) run the resulting batch script.
c:\test> dir /x > mybrokenfiles.lst
$ cat mybrokenfiles.lst | perl -lne 'next if not /MOV/; s/^.{1,39}//; s/^/ren /; s/ (\d\d);(\d\d);(\d\d)/_$1-$2-$3/; print' > fixmybrokenfiles.bat
c:\test> fixmybrokenfiles.bat
The perl commandline assumes a very particular input format, so if the DOS listing shows long filenames in something other than the "21;26;00.mov" format, it probably won't do exactly what you want. If you try it, double-check that the batch script looks right before running it.
If you are comfortable with perl (or sed/awk, python, whatever), you can script this yourself. But if DIR/X doesn't show the short filenames, your system has them disabled, and this solution won't help.
Original answer: not useful with what we know now, but if you copy this sort of file off of OSX again, you can use this BEFORE the copy as a preventative step.
I use the commandline a lot on both Windows and Linux systems. There's a handy perl script floating around the internet that allows batch file renames using standard perl regex's (google for rename.pl to find it).
Under Cygwin on windows, use this in the directory your files are located in to rename them:
$ ls
clip-2009-10-01 21;26;00.mov
$ rename.pl 'tr/ ;/_-/;' *
$ ls
clip-2009-10-01_21-26-00.mov
Pretty sure my version came from the Perl Cookbook:
#!/usr/bin/perl -w
# rename - Larry'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 $_;
}
Best Answer
You're going to run in some problems if you want to rename files and directories at the same time. Renaming just a file is easy enough. But you want to make sure the directories are also renamed. You can't simply
mv Motörhead/Encöding Motorhead/Encoding
sinceMotorhead
won't exist at the time of the call.So, we need a depth-first traversal of all files and folders, and then rename the current file or folder only. The following works with GNU
find
and Bash 4.2.42 on my OS X.You may change the regex by using
new="${f//[\\\/\:\*\?\"<>|]/}"
if you want to replace anything that Windows cannot handle.Save this script as
rename.sh
, make it executable withchmod +x rename.sh
. Then, call it likerename.sh /some/path
.Make sure to resolve any file name collisions (“
Notice
” announcements).If you're absolutely sure it does the right replacements, remove the
echo
from the script to actually rename things instead of just printing what it does.To be safe, I'd recommend testing this on a small subset of files first.
Options explained
To explain what goes on here:
-depth
will ensure directories are recursed depth-first, so we can "roll up" everything from the end. Usually,find
traverses differently (but not breadth-first).-print0
ensures thefind
output is null-delimited, so we can read it withread -d ''
into thefile
variable. Doing so helps us deal with all kinds of weird file names, including ones with spaces, and even newlines.dirname
. Don't forget to always quote your variables properly, otherwise any path with spaces or globbing characters would break this script.basename
.$f
using Bash's string replacement capabilities. Invalid means anything that's not a lower- or uppercase letter, a digit, a slash (\/
), a dot (\.
), an underscore, or a minus-hyphen.$f
is already clean (the cleaned name is identical to the current name), skip it.$new
already exists in directory$d
(e.g., you have files namedresume
andrésumé
in the same directory), issue a warning. You don't want to rename it, because, on some systems,mv foo foo
causes a problem. Otherwise,Since this will only act on the deepest hierarchy, renaming
Motörhead/Encöding
toMotorhead/Encoding
is done in two steps:mv Motörhead/Encöding Motörhead/Encoding
mv Motörhead Motorhead
This ensures all replacements are done in the correct order.
Example files and test run
Let's assume some files in a base folder called
test
:Here is the output from a run in debug mode (with the
echo
in front of themv
), i.e., the commands that would be called, and the collision warnings:Notice the absence of messages for
with-hyphen.txt
,schedule
, andtest
itself.