Gentoo Forums
Gentoo Forums
Gentoo Forums
Quick Search: in
Rename files but not extensions?
View unanswered posts
View posts from last 24 hours

Goto page Previous  1, 2  
Reply to topic    Gentoo Forums Forum Index Desktop Environments
View previous topic :: View next topic  
Author Message
John R. Graham
Administrator
Administrator


Joined: 08 Mar 2005
Posts: 4849
Location: Somewhere over Atlanta, Georgia

PostPosted: Wed Jan 25, 2012 4:10 pm    Post subject: Reply with quote

How many Gentoo scripters does it take to screw in a light bulb? :wink:

I love puzzles like this. I was about to reply before RazielFMX beat me to it that pure bash didn't have the pattern matching capability to do the whole thing, lacking rich regular expression support. It's difficult in sed and AWK as well but easy in Perl. The complexity comes in because of preserving the single '.' that separates the name from the extension but converting all others to '_'. (Dark Foo, do I have that correct?)

RazielFMX's solution is close but doesn't handle all the special cases (e.g., file name "a.b.c.d").

This is scripting, so my tendency is to use the best tool for each part. For collecting and operating on a list of files, it's hard to beat bash's mechanisms and Perl is unbeatable for pattern matching, so, with the heavy lifting done by a little Perl:
test.pl:
chomp;
s/\s/_/g;
if (($Name, $Ext) = m/^(.*)\.(.*)/) {
    $Name =~ s/\./_/g;
    print "$Name.$Ext\n";
} else {
    print "$_\n";
}
then borrowing from Naib's original one liner get's you this
Code:
for f in *; do mv "$f" $(echo $f | perl -n test.pl); done
There are endless arguments on efficiency and style, but on the argument of capability, I couldn't come up with a pure bash implementation that would do it at all. Have I missed something?

- John
_________________
Yoda: "Intentionally left blank, this space is."


Last edited by John R. Graham on Wed Jan 25, 2012 6:19 pm; edited 1 time in total
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Wed Jan 25, 2012 6:12 pm    Post subject: Reply with quote

Ah. I missed the '.' in the original request and thought it was a random space before an ill placed end of sentence. My bad. Revised, pure Perl implementation below, with special consideration for hidden files. Also note this code will not work right if you have mixed directories and files in the target. If you need that, I can modify to be directory friendly.

What you have:

Code:

$ ls -1a
.
..
a b
a_b_c_d
a.b.c.d
a b. c d.txt
.foo
.foo bar
.foo bar.txt
.foo_foo.txt
.foo.foo.txt
.foo.txt
test.txt


When you run:

Code:

$ ~/tmp/stripSpaces.pl
[WARN] .foo_foo.txt already exists; skipping rename of '.foo.foo.txt' to '.foo_foo.txt'
[INFO] Renaming 'a b. c d.txt' to 'a_b__c_d.txt'
[INFO] Renaming '.foo bar.txt' to '.foo_bar.txt'
[INFO] Renaming 'a b' to 'a_b'
[INFO] Renaming 'a.b.c.d' to 'a_b_c.d'
[INFO] Renaming '.foo bar' to '.foo_bar'


New contents:

Code:

$ ls -1a
.
..
a_b
a_b_c_d
a_b_c.d
a_b__c_d.txt
.foo
.foo_bar
.foo_bar.txt
.foo_foo.txt
.foo.foo.txt
.foo.txt
test.txt


My source code:

Code:
#!/usr/bin/perl -w

use strict;
use Cwd qw(getcwd);
use File::Copy qw(move);

my $dir = getcwd();

opendir(DIR, $dir) or die $!;

my $exitCode = 0;
while (my $file = readdir(DIR)) {
    next if ($file =~ /^\.+$/o); #Skip special dir refs

    my $new;

    if ($file =~ /^\.(.+)$/o) {
        my $tmp = $1;
        $new = '.' . &processFilename($tmp);
    } else {
        $new = &processFilename($file);
    }

    next if ($new eq $file); #No changes were made

    if (-e $new) {
        print STDERR "[WARN] $new already exists; skipping rename of '$file' to '$new'\n";
        next;
    }
   
    print STDERR "[INFO] Renaming '$file' to '$new'\n";
   
    my $check = move($file, $new);
    if (! $check) {
        print STDERR "[WARN] Unable to rename '$file' to '$new': $!\n";
        # File::Copy::Move can leave artifacts on the FS
        if (-e $new) {
            unlink $new;
        }
       
        $exitCode = -1 if ($exitCode == 0);
    }

}

closedir(DIR);

exit($exitCode);

sub processFilename {
    my $file = shift;

    my $new;

    if ($file =~ /^(.+)\.(\S+)$/o) {
        my $base = $1;
        my $ext = $2;
       
        $base =~ s/(?:\s|\.)/_/go;
        $new = join('.', $base, $ext);
    } else {
        ($new = $file) =~ s/(?:\s|\.)/_/go;
    }

    return $new;
}
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Wed Jan 25, 2012 6:23 pm    Post subject: Reply with quote

John R. Graham wrote:
The complexity comes in because of preserving the single '.' that separates the name from the extension but converting all others to '_'. (Dark Foo, do I have that correct?)


What have I started ;)

Yes thats correct John, Guess my request was not as simple as i first thought :( Still all a learning experience, if i wanted easy I could have stayed with Windows ;)

Thanks for the script RazielFMX, much appreciated :)

If i read it right, and I'm probably not, it looks at though it will do files only atm, or will it do folders too?

I could do files then folders but both at same time would be faster for me
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Wed Jan 25, 2012 9:13 pm    Post subject: Reply with quote

Do you want it to tree walk, or simply executing only in the current directory is ok?

And you are right, it will not work on directories in the event of a failure, however, it WILL work on directories if everything goes right.

Let me know what your spec is and I will modify it accordingly.
Back to top
View user's profile Send private message
Naib
Advocate
Advocate


Joined: 21 May 2004
Posts: 3891
Location: UK - Birmingham

PostPosted: Wed Jan 25, 2012 9:20 pm    Post subject: Reply with quote

To walk a tree with bash(4)

Code:

shopt -s globstar; for f in **/*; do  t="${f// /_}"; mv "$f" "${t//./_}.${f##*.}"; done

should do it
_________________
Quote:
Voting holds no real power, he who counts the votes has the true power.

Weaver Projects
whats the difference between 9/11 and a cow?
u stop milking a cow after 10 years
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Wed Jan 25, 2012 9:26 pm    Post subject: Reply with quote

well seeing as i am lazy i would like to start from my raid5 and have it look at everything below

/storage
/pictures
/music
/3D_APPS

so on and so forth

I will have to take a look in the morning - thanks to everyone who has posted suggestions, i really appreciate your help :)
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
John R. Graham
Administrator
Administrator


Joined: 08 Mar 2005
Posts: 4849
Location: Somewhere over Atlanta, Georgia

PostPosted: Wed Jan 25, 2012 9:27 pm    Post subject: Reply with quote

Naib, with your latest script, test file name "a b c.d e f" is renamed to "a_b_c_d_e_f.d e f", not exactly what the OP is requesting.

- John
_________________
Yoda: "Intentionally left blank, this space is."
Back to top
View user's profile Send private message
Naib
Advocate
Advocate


Joined: 21 May 2004
Posts: 3891
Location: UK - Birmingham

PostPosted: Wed Jan 25, 2012 11:09 pm    Post subject: Reply with quote

well some assumptions will have to be made. its from windows so the last decimal is going to be use for extension .###
_________________
Quote:
Voting holds no real power, he who counts the votes has the true power.

Weaver Projects
whats the difference between 9/11 and a cow?
u stop milking a cow after 10 years
Back to top
View user's profile Send private message
BoneKracker
Veteran
Veteran


Joined: 14 Mar 2006
Posts: 1271
Location: U.S.A.

PostPosted: Wed Jan 25, 2012 11:18 pm    Post subject: Reply with quote

:lol:

Ever seen the episode of Star Trek with the "Neural Neutralizer"? Perl is the "Neural Neutralizer" of scripting languages. It can do anything, but it will fry your brain in the process. :P
_________________
Obama killed bin Laden like Nixon was the first man on the Moon.
Back to top
View user's profile Send private message
John R. Graham
Administrator
Administrator


Joined: 08 Mar 2005
Posts: 4849
Location: Somewhere over Atlanta, Georgia

PostPosted: Thu Jan 26, 2012 1:06 am    Post subject: Reply with quote

It's kind of like Cyrillic: until you know the symbols, it just looks like gibberish. :wink:

Well, actually, it still looks like gibberish. But, it is powerful. Little while ago, I became "upstream" on a medium sized Perl project (app-backup/flexbackup) so I've been practicing. :P

- John
_________________
Yoda: "Intentionally left blank, this space is."
Back to top
View user's profile Send private message
BoneKracker
Veteran
Veteran


Joined: 14 Mar 2006
Posts: 1271
Location: U.S.A.

PostPosted: Thu Jan 26, 2012 2:58 am    Post subject: Reply with quote

I thought there was a 'rename' in perl, specifically for file renaming, perhaps a module.
_________________
Obama killed bin Laden like Nixon was the first man on the Moon.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Thu Jan 26, 2012 3:36 am    Post subject: Reply with quote

BoneKracker wrote:
I thought there was a 'rename' in perl, specifically for file renaming, perhaps a module.


There is, but it is recommend to use the File::Copy::move instead as rename behavior varies from system to system.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Thu Jan 26, 2012 4:28 pm    Post subject: Reply with quote

One fully featured Perl script, as promised.

Code:

#!/usr/bin/perl -w

# Author: RazielFMZ 26-Jan-2012
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

use strict;
use Getopt::Long qw(:config bundling);
use File::Copy qw(move);
use File::Find qw(find);
use File::Basename qw(basename);

#
# Usage
#

my $opts = {};
&GetOptions(
    $opts,
    "help|h",
    "dir=s@",
    "recurse|r"
) or die &getUsage();

if (defined $opts->{'help'} && $opts->{'help'}) {
    print STDERR &getUsage();
    exit(1);
}

if (! defined $opts->{'dir'}) {
    die "--dir is required";
} else {
    #Validate
    foreach my $dir (@{$opts->{'dir'}}) {
        if (! -d $dir) {
            die "$dir is not a valid directory";
        }
    }
}

#
# Main
#
my $exitCode = 0;

if ($opts->{'recurse'}) {
    print STDERR "[INFO] Operating in recursive mode, delegating to find\n";
    find({
        wanted => sub {
            my $base = basename($File::Find::name);

            ##Skip magic dirs
            if ($base !~ /^\.+$/o) {
                my $check = &processEntry($File::Find::name);
                if (! $check) {
                    $exitCode = -1 if ($exitCode == 0);
                }
            }
        },
        follow => 0,
        bydepth => 1,
        no_chdir => 1,
    }, @{$opts->{'dir'}});
} else {
    print STDERR "[INFO] Recursive mode disabled\n";

    foreach my $dir (@{$opts->{'dir'}}) {
        my $dc = opendir(DIR, $dir);
        if (! $dc) {
            print STDERR "[ERROR] Failure to open $dir: $!\n";
            $exitCode = -1 if ($exitCode == 0);
            next;
        }

        while (my $entry = readdir(DIR)) {
            next if ($entry =~ /^\.+$/o);

            my $tmp = join('/', $dir, $entry);
            my $check = &processEntry($tmp);
            if (! $check) {
                $exitCode = -1 if ($exitCode == 0);
            }
        }

        closedir(DIR);
    }
}

exit($exitCode);

#
# Methods
#

sub processEntry {
    my $file = shift;

    my $new;

    if ($file =~ /^\.(.+)$/o) {
        my $tmp = $1;
        $new = '.' . &processFilename($tmp);
    } else {
        $new = &processFilename($file);
    }

    return 1 if ($new eq $file);

    if (-e $new) {
        print STDERR "[WARN] $new already exists; skipping rename of '$file' to '$new'\n";
        return 1;
    }
   
    print STDERR "[INFO] Renaming '$file' to '$new'\n";
   
    my $check = move($file, $new);

    if (! $check) {
        print STDERR "[WARN] Unable to rename '$file' to '$new': $!\n";
        return undef;
    }

    return 1;
}

sub processFilename {
    my $file = shift;

    my $new;

    if ($file =~ /^(.+)\.(\S+)$/o) {
        my $base = $1;
        my $ext = $2;
       
        $base =~ s/(?:\s|\.)/_/go;
        $new = join('.', $base, $ext);
    } else {
        ($new = $file) =~ s/(?:\s|\.)/_/go;
    }

    return $new;
}

sub getUsage {

    my $scriptname = basename($0);

    my $usage = <<_USAGE_END_;

Usage: $scriptname

    h|help    : Print this screen
    dir <dir> : Directory to execute under, this can be specified multiple
                times for more than one directory.
    r|recurse : Recursively walk the directory tree.  Symlinks are ignored.

_USAGE_END_

    return $usage;
}



You can turn on and off the recursive mode, and specify multiple directories by specifying multiple --dir arguments.

Code:

$ ./stripSpaces.pl --help

Usage: stripSpaces.pl

    h|help    : Print this screen
    dir <dir> : Directory to execute under, this can be specified multiple
                times for more than one directory.
    r|recurse : Recursively walk the directory tree.  Symlinks are ignored.



Last edited by RazielFMX on Thu Jan 26, 2012 4:35 pm; edited 1 time in total
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Thu Jan 26, 2012 4:34 pm    Post subject: Reply with quote

sweet - will try it out shortly and report back :)
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Thu Jan 26, 2012 4:35 pm    Post subject: Reply with quote

There was a small bug I just patched and did an in-post edit. Please use the newest version :-)
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Fri Jan 27, 2012 5:46 pm    Post subject: Reply with quote

works quite well, it has left a few folders with a dot in its name, what would i have to add for it to change - to _ ?
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Fri Jan 27, 2012 9:16 pm    Post subject: Reply with quote

it leaves the . on folders due to it thinking it is an extension. I have patched it (with support for symlinks as well) and added the '-' character. I switched the regex to be globally stored so you or I can tweak it easily and not have look into the methods.

Code:

#!/usr/bin/perl -w

# Author: RazielFMZ 27-Jan-2012, v1.1
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

use strict;
use Getopt::Long qw(:config bundling);
use File::Copy qw(move);
use File::Find qw(find);
use File::Basename qw(basename);

#Global Vars
our $RENAME_REGEX = qr/(?:\s|[.-])/;

#
# Usage
#

my $opts = {};
&GetOptions(
    $opts,
    "help|h",
    "dir=s@",
    "recurse|r"
) or die &getUsage();

if (defined $opts->{'help'} && $opts->{'help'}) {
    print STDERR &getUsage();
    exit(1);
}

if (! defined $opts->{'dir'}) {
    die "--dir is required";
} else {
    #Validate
    foreach my $dir (@{$opts->{'dir'}}) {
        if (! -d $dir) {
            die "$dir is not a valid directory";
        }
    }
}

#
# Main
#
my $exitCode = 0;

if ($opts->{'recurse'}) {
    print STDERR "[INFO] Operating in recursive mode, delegating to find\n";
    find({
        wanted => sub {
            my $base = basename($File::Find::name);

            ##Skip magic dirs
            if ($base !~ /^\.+$/o) {
                my $check = &processEntry($File::Find::name);
                if (! $check) {
                    $exitCode = -1 if ($exitCode == 0);
                }
            }
        },
        follow => 0,
        bydepth => 1,
        no_chdir => 1,
    }, @{$opts->{'dir'}});
} else {
    print STDERR "[INFO] Recursive mode disabled\n";

    foreach my $dir (@{$opts->{'dir'}}) {
        my $dc = opendir(DIR, $dir);
        if (! $dc) {
            print STDERR "[ERROR] Failure to open $dir: $!\n";
            $exitCode = -1 if ($exitCode == 0);
            next;
        }

        while (my $entry = readdir(DIR)) {
            next if ($entry =~ /^\.+$/o);

            my $tmp = join('/', $dir, $entry);
            my $check = &processEntry($tmp);
            if (! $check) {
                $exitCode = -1 if ($exitCode == 0);
            }
        }

        closedir(DIR);
    }
}

exit($exitCode);

#
# Methods
#

sub processEntry {
    my $file = shift;

    my $treatAsDir;
    if (-l $file) {
        my $target = readlink($file);
        #If readlink returns undef and we know we have a symlink,
        #we have a link with no target, just treat is like a file
        $treatAsDir = (defined $target && -d $target) ? 1 : 0;
    } elsif (-d $file) {
        $treatAsDir = 1;
    } else {
        $treatAsDir = 0;
    }

    my $new;

    if ($file =~ /^\.(.+)$/o) {
        my $tmp = $1;
        $new = '.' . &processFilename($tmp, $treatAsDir);
    } else {
        $new = &processFilename($file, $treatAsDir);
    }

    return 1 if ($new eq $file);

    if (-e $new) {
        print STDERR "[WARN] $new already exists; skipping rename of '$file' to '$new'\n";
        return 1;
    }
   
    print STDERR "[INFO] Renaming '$file' to '$new'\n";
   
    my $check = move($file, $new);

    if (! $check) {
        print STDERR "[WARN] Unable to rename '$file' to '$new': $!\n";
        return undef;
    }

    return 1;
}

sub processFilename {
    my $file = shift;
    my $treatAsDir = shift;

    my $new;
    if ((! $treatAsDir) && $file =~ /^(.+)\.(\S+)$/o) {
        my $base = $1;
        my $ext = $2;
       
        $base =~ s/$RENAME_REGEX/_/go;
        $new = join('.', $base, $ext);
    } else {
        ($new = $file) =~ s/$RENAME_REGEX/_/go;
    }

    return $new;
}

sub getUsage {

    my $scriptname = basename($0);

    my $usage = <<_USAGE_END_;

Usage: $scriptname

    h|help    : Print this screen
    dir <dir> : Directory to execute under, this can be specified multiple
                times for more than one directory.
    r|recurse : Recursively walk the directory tree.  Symlinks are ignored.

_USAGE_END_

    return $usage;
}
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Fri Jan 27, 2012 9:17 pm    Post subject: Reply with quote

Thanks, will test it out in the morning, rather tired atm, but not as tired as if I had to rename all the files, your script it a god send :)
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Fri Jan 27, 2012 9:24 pm    Post subject: Reply with quote

Dark Foo wrote:
Thanks, will test it out in the morning, rather tired atm, but not as tired as if I had to rename all the files, your script it a god send :)


Anytime!
Back to top
View user's profile Send private message
Dark Foo
l33t
l33t


Joined: 21 Nov 2008
Posts: 921
Location: 127.0.0.1

PostPosted: Sat Jan 28, 2012 1:35 pm    Post subject: Reply with quote

Looking good so far, have only tested it on one dir, just in case ;) but looks to be performing its job perfectly :)
_________________
I know 43 ways to kill with a SKITTLE, so taste my rainbow bitch.
Back to top
View user's profile Send private message
RazielFMX
l33t
l33t


Joined: 23 Apr 2005
Posts: 672
Location: NY, USA

PostPosted: Mon Jan 30, 2012 7:25 pm    Post subject: Reply with quote

Glad to hear it.
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    Gentoo Forums Forum Index Desktop Environments All times are GMT
Goto page Previous  1, 2
Page 2 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum