LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   How to shorten file name recursively using grep and rename (https://www.linuxquestions.org/questions/linux-newbie-8/how-to-shorten-file-name-recursively-using-grep-and-rename-4175437345/)

alaw005 11-15-2012 05:23 PM

How to shorten file name recursively using grep and rename
 
Hi, I have spent hours trying to solve what I thought was a simple problem. I have a large number of file names that are too long in a number of folders.

I am trying to shorten the length of each file name using:

Code:

find . -name '*' -print0 | xargs -0 rename -n 's/^(.*\/?).(.{25})(\..*)$/$1$2$3/' *
The regex doesn't work, The first part of the regex is supposed to keep the file path, the second part is supposed to truncate the file name to 25 characters and the last part of supposed to keep the extension but its just not working. I have tried to find answer by searching internet (for almost 3 hours!) to no avail.

NB: The path and file name may have spaces in them. Also, I am using the "rename -n" flag to ensure no changes made while testing. And I'm on ubuntu.

Please help save my sanity.

pixellany 11-15-2012 08:38 PM

I can't find any reference to a Linux "rename" command with that syntax---can you provide a link?

Assuming that the syntax is legal, it looks like the SED "s" command. But--using SED rules--I see a bunch of problems.

Finally, I wonder about using find when you are accepting any and all results.

pixellany 11-15-2012 08:58 PM

I did find the "s" syntax in the PERL rename command......

towheedm 11-15-2012 09:01 PM

Here's the regex for what you want:
Code:

's/(.*\/)(.{25}).*([.].*$)/$1$2$3/'
Pixellany: rename is an external command although it's not part of coreutils or any of the common Linux util. It is however a library call in stdio.h so Perl probably just uses that. It's included in Debian by default.

pixellany 11-16-2012 07:47 AM

Well....

My system has a "rename" command, but it does not seem to recognize the "s" syntax (and it's not in the man page)

Google finds a paucity of info on the version used above

Are you saying that the above version of rename is in fact a PERL script?

towheedm 11-16-2012 08:24 PM

You're correct!. It's a PERL script. Here's what I dug up on Debian Squeeze:
Code:

$ file /usr/bin/rename
/usr/bin/rename: symbolic link to `/etc/alternatives/rename'
$ ls -l /etc/alternatives/rename
lrwxrwxrwx 1 root root 16 Nov 19  2011 /etc/alternatives/rename -> /usr/bin/prename
$ file /usr/bin/prename
/usr/bin/prename: a /usr/bin/perl -w script text executable

The script:
Code:

$ cat /usr/bin/prename
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
#  from Larry Wall's original script eg/rename from the perl source.
#
#  This script is free software; you can redistribute it and/or modify it
#  under the same terms as Perl itself.
#
# Larry(?)'s RCS header:
#  RCSfile: rename,v  Revision: 4.1  Date: 92/08/07 17:20:30
#
# $RCSfile: rename,v $$Revision: 1.5 $$Date: 1998/12/18 16:16:31 $
#
# $Log: rename,v $
# Revision 1.5  1998/12/18 16:16:31  rmb1
# moved to perl/source
# changed man documentation to POD
#
# Revision 1.4  1997/02/27  17:19:26  rmb1
# corrected usage string
#
# Revision 1.3  1997/02/27  16:39:07  rmb1
# added -v
#
# Revision 1.2  1997/02/27  16:15:40  rmb1
# *** empty log message ***
#
# Revision 1.1  1997/02/27  15:48:51  rmb1
# Initial revision
#

use strict;

use Getopt::Long;
Getopt::Long::Configure('bundling');

my ($verbose, $no_act, $force, $op);

die "Usage: rename [-v] [-n] [-f] perlexpr [filenames]\n"
    unless GetOptions(
        'v|verbose' => \$verbose,
        'n|no-act'  => \$no_act,
        'f|force'  => \$force,
    ) and $op = shift;

$verbose++ if $no_act;

if (!@ARGV) {
    print "reading filenames from STDIN\n" if $verbose;
    @ARGV = <STDIN>;
    chop(@ARGV);
}

for (@ARGV) {
    my $was = $_;
    eval $op;
    die $@ if $@;
    next if $was eq $_; # ignore quietly
    if (-e $_ and !$force)
    {
        warn  "$was not renamed: $_ already exists\n";
    }
    elsif ($no_act or rename $was, $_)
    {
        print "$was renamed as $_\n" if $verbose;
    }
    else
    {
        warn  "Can't rename $was $_: $!\n";
    }
}

__END__

=head1 NAME

rename - renames multiple files

=head1 SYNOPSIS

B<rename> S<[ B<-v> ]> S<[ B<-n> ]> S<[ B<-f> ]> I<perlexpr> S<[ I<files> ]>

=head1 DESCRIPTION

C<rename>
renames the filenames supplied according to the rule specified as the
first argument.
The I<perlexpr>
argument is a Perl expression which is expected to modify the C<$_>
string in Perl for at least some of the filenames specified.
If a given filename is not modified by the expression, it will not be
renamed.
If no filenames are given on the command line, filenames will be read
via standard input.

For example, to rename all files matching C<*.bak> to strip the extension,
you might say

        rename 's/\.bak$//' *.bak

To translate uppercase names to lower, you'd use

        rename 'y/A-Z/a-z/' *

=head1 OPTIONS

=over 8

=item B<-v>, B<--verbose>

Verbose: print names of files successfully renamed.

=item B<-n>, B<--no-act>

No Action: show what files would have been renamed.

=item B<-f>, B<--force>

Force: overwrite existing files.

=back

=head1 ENVIRONMENT

No environment variables are used.

=head1 AUTHOR

Larry Wall

=head1 SEE ALSO

mv(1), perl(1)

=head1 DIAGNOSTICS

If you give an invalid Perl expression you'll get a syntax error.

=head1 BUGS

The original C<rename> did not check for the existence of target filenames,
so had to be used with care.  I hope I've fixed that (Robin Barker).

=cut

The manpage:
Code:

$ man rename
RENAME(1)              Perl Programmers Reference Guide              RENAME(1)

NAME
      rename - renames multiple files

SYNOPSIS
      rename [ -v ] [ -n ] [ -f ] perlexpr [ files ]

DESCRIPTION
      "rename" renames the filenames supplied according to the rule specified
      as the first argument.  The perlexpr argument is a Perl expression
      which is expected to modify the $_ string in Perl for at least some of
      the filenames specified.  If a given filename is not modified by the
      expression, it will not be renamed.  If no filenames are given on the
      command line, filenames will be read via standard input.

      For example, to rename all files matching "*.bak" to strip the
      extension, you might say

              rename 's/\.bak$//' *.bak

      To translate uppercase names to lower, you'd use

              rename 'y/A-Z/a-z/' *

OPTIONS
      -v, --verbose
              Verbose: print names of files successfully renamed.

      -n, --no-act
              No Action: show what files would have been renamed.

      -f, --force
              Force: overwrite existing files.

ENVIRONMENT
      No environment variables are used.

AUTHOR
      Larry Wall

SEE ALSO
      mv(1), perl(1)

DIAGNOSTICS
      If you give an invalid Perl expression you'll get a syntax error.

BUGS
      The original "rename" did not check for the existence of target
      filenames, so had to be used with care.  I hope I've fixed that (Robin
      Barker).

perl v5.10.1                      2011-12-20                        RENAME(1)


alaw005 11-17-2012 02:36 AM

Thanks towheedm, the regex worked a treat. I didn't realise I was using a PERL script, I came across it in a post somewhere. Once again, thanks for the help.

mbnoimi 12-23-2015 09:27 AM

Same problem
 
Guys I've same problem but I don't know how to use the above script :(

I've many files with long file name so rsync and many applications don't work fine with these long names.

May I get so help,

norobro 12-23-2015 12:03 PM

Did you try the find command in post #1 using the regex from post #4?
Code:

find . -name '*' -print0 | xargs -0 rename -n 's/(.*\/)(.{25}).*([.].*$)/$1$2$3/' *
Note the "-n".

mbnoimi 12-23-2015 12:40 PM

Quote:

Originally Posted by norobro (Post 5468073)
Did you try the find command in post #1 using the regex from post #4?
Code:

find . -name '*' -print0 | xargs -0 rename -n 's/(.*\/)(.{25}).*([.].*$)/$1$2$3/' *
Note the "-n".

I did that but the length of file name took as full path not just the file name only see this example:
Code:

./.local/share/Trash/files/cache/images/248741.banner.jpg renamed as ./.local/share/Trash/files/cache/images/248741.banne.jpg
This is totally wrong! 248741.banner.jpg shouldn't be rename!

UPDATED: Another wrong rename
Code:

./.kodi/userdata/Thumbnails/d/d524604e.jpg renamed as ./.kodi/userdata/Thumbnails/d/d52.jpg

norobro 12-23-2015 01:10 PM

Try changing the second capture group from (.{25}) to (.{0,25})

mbnoimi 12-23-2015 02:28 PM

Quote:

Originally Posted by norobro (Post 5468099)
Try changing the second capture group from (.{25}) to (.{0,25})

Still doesn't work
Code:

./.kodi/userdata/Thumbnails/8/8afbda1a.jpg renamed as ./.kodi/userdata/Thumbnails/8/8afbda1a

norobro 12-23-2015 03:32 PM

Dunno.

Works fine here with sed:
Code:

$ echo "./.kodi/userdata/Thumbnails/8/8afbda1a.jpg" | sed -r 's/(.*\/+)(.{0,25}).*([.].*$)/\1\2\3/'
./.kodi/userdata/Thumbnails/8/8afbda1a.jpg

And in a simple script, to check perl:
Code:

#!/usr/bin/perl

my($text) = "./.kodi/userdata/Thumbnails/8/8afbda1a.jpg";
$text =~ m/(.*\/+)(.{0,25}).*([.].*$)/;
print $1 . $2 . $3 ."\n";



All times are GMT -5. The time now is 10:55 AM.