3

I've got about 15 lots of 100+ directories, specifically using pseudocode how can you:

rename * *suffix  
rename * prefix*  
rename * CAPITALIZE*  

BUT tips or links to tutorials specific to working with multiple files and directories would be appreciated.

6 Answers 6

5

Many linux/unix systems come with a rename or rename.pl or prename perl-script that allows renaming via perl expressions. On Debian systems, it's installed with the perl package.

$ rename
Usage: rename [-v] [-n] [-f] perlexpr [filenames]

The magic happens in the perlexpr argument. This is essentially a short bit of perl code that will operate on each filename. You don't need to provide any loops, just write code that transforms your input into the desired output. (Be sure and quote the expression; I use single-quotes to protect against shell expansion unless it's absolutely necessary.)

So, for your examples:

  • rename * *suffix

    $ rename 's/$/suffix/' *
              ^ ^  ^^^^
              | |    +-- your suffix goes here
              | +------- indicates end-of-string
              +--------- perl's substitution operator (s/from-regex/to/)
    
  • rename * prefix*

    $ rename 's/^/prefix/' *
                ^  ^^^^
                |    +-- your prefix goes here
                +------- indicates beginning-of-string
    
  • rename * CAPITALIZE*

    $ rename '$_ = uc' *
              ^^^^ ^^
               |    +-- perl builtin function to capitalize a string
               +------- replaces input filename
    

See more examples from the O'Reilly Unix Power Tools book.

3
  • But, with care, it can all be done in one operation! Oct 31, 2009 at 4:57
  • sure! i'm assuming three different examples, not three operations in a single example. Oct 31, 2009 at 5:11
  • If you are unsure about the regex you are using, add the -v and -n flags, which will show the renames that would happen, but not actually perform them. Feb 14, 2013 at 12:44
2

This will work for prefix and suffix additions to all file names.

find /base/dir/path -type f -exec mv {} prefix{}suffix \;
#                                    -- ^^^^^^--^^^^^^

For the capitalization you could probably write a short bash loop that will used something like a sed to change the letter case and then move.

I will suggest reading,

  1. Advanced Bash Scripting Guile
  2. Perl scripting
2

I'd use Perl - and specifically this modified version of a 'rename' script that was in the 1st Edition of the Camel book (but dropped from the second and third editions for lack of space).

Usage:

find . -type f -print0 | xargs -0 rename 's%/([^/]+)$%/prefix\U${1}\Esuffix%'

Which, being translated, means:

  • find all the files, even those with blanks in their name, and handle them properly.
  • for each file name, find the name after the last slash, remembering it as '${1}'
  • replace the name with 'prefix', the upper-case version of what was remembered, and 'suffix'

So, the whole job can be done in one command with a sufficiently versatile tool.


#!/Users/jleffler/perl/v5.10.0/bin/perl -w
#
# @(#)$Id: rename.pl,v 1.7 2008/02/16 07:53:08 jleffler Exp $
#
# Rename files using a Perl substitute or transliterate command

use strict;
use Getopt::Std;

my(%opts);
my($usage) = "Usage: $0 [-fnxV] perlexpr [filenames]\n";
my($force) = 0;
my($noexc) = 0;
my($trace) = 0;

die $usage unless getopts('fnxV', \%opts);

if ($opts{V})
{
    printf "%s\n", q'RENAME Version $Revision: 1.7 $ ($Date: 2008/02/16 07:53:08 $)';
    exit 0;
}
$force = 1 if ($opts{f});
$noexc = 1 if ($opts{n});
$trace = 1 if ($opts{x});

my($op) = shift;
die $usage unless defined $op;

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

for (@ARGV)
{
    if (-e $_ || -l $_)
    {
        my($was) = $_;
        eval $op;
        die $@ if $@;
        next if ($was eq $_);
        if ($force == 0 && -f $_)
        {
            print STDERR "rename failed: $was - $_ exists\n";
        }
        else
        {
            print "+ $was --> $_\n" if $trace;
            print STDERR "rename failed: $was - $!\n"
                unless ($noexc || rename($was, $_));
        }
    }
    else
    {
        print STDERR "$_ - $!\n";
    }
}

For whatever it is worth, I ran into a persistent failure that was not dreadfully informative when I tried:

$ find . -type f -print0 | xargs -0 rename 's/.*/prefix$&suffix/'
rename failed: ./xxx-32 - No such file or directory
rename failed: ./xxx-64 - No such file or directory
rename failed: ./xxx.c - No such file or directory
rename failed: ./xxx.c~ - No such file or directory
$

That was odd - I can see those files...

Using the '-x' option to 'rename' told me what the trouble was, though:

rename failed: ./xxx-32 - No such file or directory
rename failed: ./xxx-64 - No such file or directory
rename failed: ./xxx.c - No such file or directory
rename failed: ./xxx.c~ - No such file or directory
+ ./xxx-32 --> prefix./xxx-32suffix
+ ./xxx-64 --> prefix./xxx-64suffix
+ ./xxx.c --> prefix./xxx.csuffix
+ ./xxx.c~ --> prefix./xxx.c~suffix

Oh right - there isn't a sub-directory called 'prefix.' in my current directory!

2
  • +1 for find -print0 | xargs -0 Oct 31, 2009 at 9:35
  • @Jonathan Leffler: I feel that you have both blessed and tormented me, as your script is a godsend ... yet I feel I must learn another language, namely PERL, immediately. :) Thank You, very much. Oct 31, 2009 at 14:25
1

For the caps-to-lowercase, how about:

for filename in *                # Traverse all files in directory.
do
   fname=`basename $filename`
   n=`echo $fname | tr A-Z a-z`  # Change name to lowercase.
   if [ "$fname" != "$n" ]       # Rename only files not already lowercase.
   then
     mv $fname $n
   fi  
done   

Stolen shamelessly from the Linux Documentation Project: http://tldp.org/LDP/abs/html/textproc.html

Has to be easier (and resource-lighter) than using Perl.

1
  • 1
    That will not change the names of the files, just uppercase the contents of the files (and dump that to stdout). Oct 31, 2009 at 9:36
0

I like Perl rename the best, if you have it or can get it.

Here are some UPPERCASE variations:

# take out the echo to make these do the work

# find + xargs + bash (v4) + mv
# requires bash 4 for the ^^ uppercasing feature
find * -type f -print0 | xargs -0 bash -c 'for f in "$@"; do dir="";case "$1" in (*/*) dir="${f%/*}/";;esac;filename="${f##*/}"; echo mv "$f" "$dir${filename^^}"; done' mv

# find + xargs + sh + dirname + basename + tr + mv
# maybe easier to understand than above, but noticeably slower due to 3 forks per file
find * -type f -print0 | xargs -0 sh -c 'for f in "$@"; do echo mv "$f" "$(dirname "$f")/$(printf "$(basename "$f")" | tr "[:lower:]" "[:upper:]")"; done' mv

I have been playing around with zsh lately, so here are the zmv invocations:

# If the command line shell is not zsh, the prefix each of these commands with
#    zsh -fc 'autoload -U zmv && zmv $@'
# take the -n off to make it do the work instead of just talk about it

zmv -Qn '(**/)(*)(.)' '${1}prefix${2}'
zmv -Qn '(**/)(*)(.)' '${1}${2}suffix'
zmv -Qn '(**/)(*)(.)' '${1}${(U)2}'
# without the (.) qualifier (and the enabling -Q), dirs are processed also
zmv -n '(**/)(*)' '${1}prefix${2}'
zmv -n '(**/)(*)' '${1}${2}suffix'
zmv -n '(**/)(*)' '${1}${(U)2}'
0

This rename perl script evaluates the first argument as code to rename the files given with the rest of the arguments.

#!/usr/bin/perl

($op = shift) || die "Usage: $0 perlexpr [filenames]\n";
if (!@ARGV) {
    @ARGV = <STDIN>;
    chop(@ARGV);
}
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .