nkpgmartin has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to open a directory and foreach file that has a space in the name, replace the space with a ".". Here is what I have:
opendir(DIR, "$dir") || die "cant open $dir $!\n"; my @filename = readdir (DIR); foreach $filename (@filename) { if (/ /) { $newfile =~ s/\(.*\) *\(.*\)/\./; #this may not be exactly right rename($filename, $newfile); } } closedir(DIR);
But obviously it doesn't work. What is a better way to do this? Thanks.

Replies are listed 'Best First'.
Re: How can I opendir, replace all files containing a space with .?
by VSarkiss (Monsignor) on Aug 15, 2001 at 00:29 UTC

    Single-character substitution is usually easiest with tr:

    my ($filename, $newfile); foreach $filename (readdir(DIR)) { ($newfile = $filename) =~ tr/ /./; rename($filename, $newfile); }
    As a bonus, this will replace every space with a period. (That is a bonus, right? ;-)

    Also, you want to either chdir to the target directory, or prepend the directory name to the file names. When possible, I prefer chdir:

    chdir $dir or die "Can't chdir to $dir: $!\n"; opendir(DIR, '.') or die "Can't open '.': $!\n";

    HTH

Re: How can I opendir, replace all files containing a space with .?
by maverick (Curate) on Aug 14, 2001 at 23:27 UTC
    change
    if (/ /) { $newfile =~ s/\(.*\) *\(.*\)/\./;
    to
    if ($filename =~ / /) { $newfile = $filename; $newfile =~ s/ /./g;
    and you should be good to go.

    /\/\averick
    perl -l -e "eval pack('h*','072796e6470272f2c5f2c5166756279636b672');"

Re: How can I opendir, replace all files containing a space with .?
by runrig (Abbot) on Aug 15, 2001 at 02:58 UTC
    This is a bit overkill for just renaming files in a directory, but if you want to rename files in a directory and all subdirectories (or rename directories also), you can modify the first two lines of the sub:
    use File::Find; my @dirs = qw(.); find( sub{ return if $File::Find::name eq $File::Find::topdir; $File::Find::prune=1, return if -d; if ((my $newname = $_) =~ tr/ /./) { print "Renaming $File::Find::name to $newname\n"; rename $_, $newname or die "Can't rename $_: $!"; } }, @dirs);
Re: How can I opendir, replace all files containing a space with .?
by busunsl (Vicar) on Aug 15, 2001 at 10:43 UTC
    In addition to what's been said already:

    Your code looks like you try to replace *any* number of spaces by *one* dot.
    If you want to replace one or more spaces by one dot, tr/ /./ will not work.
    You have to use s/ +/./g

Re: How can I opendir, replace all files containing a space with .?
by Hofmator (Curate) on Aug 15, 2001 at 15:13 UTC

    You already have gotten working but let me tell 'tear apart' your code, to tell you what went wrong. You should see this as a chance to learn, no offence meant.

    I put some comments into your sourcecode, always above the line which they belong to:

    # good, open with error checking opendir(DIR, "$dir") || die "can't open $dir $!\n"; # maybe name the variable @filenames - as there is more than one # or get rid of the variable altogether by saying # foreach my $oldname ( readdir(DIR) ) my @filename = readdir (DIR); # declare filename with my and give better name (see above) # foreach my $oldname (@filename) foreach $filename (@filename) { # this matches against the special variable $_ # (which you are not using) you want # if ($filename =~ / /) if (/ /) { # the regex is completely wrong you want either # s/ +/./g or tr/ /./ try to figure out the difference # and $newfile is never assigned a value so: my $newfile = $oldfile; $newfile =~ s/ +/./g; # you don't have to do the rename when you didn't # change anything, so rename($oldname, $newfile) unless $oldname eq $newfile; } } closedir(DIR);

    If you want to use this as a standalone script, I'd go for the rename program out of the Cookbook (recipe 9.9):

    #!/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 $_; }
    which can be used e.g. in the following ways (also from the Cookbook)
    % rename 's/\.orig$//' *.orig % rename 'tr/A-Z/a-z/ unless /^Make/' * % rename '$_ .= ".bad"' *.f % rename 'print "$_: "; s/foo/bar/ if <STDIN> =~ /^y/i' * % find /tmp -name '*~' -print | rename 's/^(.+)~$/.#$1/'
    This approach has several advantages, as you can specify the affected files from the command line (and don't have to work on all files in a directory). Furthermore it is very flexible due to the eval approach. Your problem could be solved with it like this:
    % rename 's/ +/./g' * or % rename 'tr/ /./' *
    Try to understand how this program works - and if you have any questions, feel free to ask here ...

    -- Hofmator

      Thanks! I appreciate the dissection. Maybe I'm just missing something, but while my script now does create a file with the . instead of the space, that file is empty. So its doing a "touch" rather than a "mv" and that is using open(NEW, ">>Test/$newfile"); what am I missing?

        In what way are you doing an open(NEW, ">>Test/$newfile") call - I thought you are renaming files with rename??

        Could you post the code you have now, please.

        -- Hofmator

Re: How can I opendir, replace all files containing a space with .?
by mugwumpjism (Hermit) on Aug 16, 2001 at 02:51 UTC

    Hmm. Be careful with this script (once corrected) - if you have two files "legalise cannabis" and "legalise.cannabis", the first file will be renamed on top of the second one!

    I'd change the middle of your loop to:

    if (-e $newfile) { print STDERR ("Warning: $filename would " ."clobber $newfile, skipping\n"); } else { rename($filename, $newfile); }