How to rename with prefix/suffix?
Asked Answered
N

11

139

How do I do mv original.filename new.original.filename without retyping the original filename?

I would imagine being able to do something like mv -p=new. original.filename or perhaps mv original.filename new.~ or whatever - but I can't see anything like this after looking at man mv / info mv pages.

Of course, I could write a shell script to do this, but isn't there an existing command/flag for it?

Niobium answered 16/10, 2008 at 11:37 Comment(0)
G
145

In Bash and zsh you can do this with Brace Expansion. This simply expands a list of items in braces. For example:

# echo {vanilla,chocolate,strawberry}-ice-cream
vanilla-ice-cream chocolate-ice-cream strawberry-ice-cream

So you can do your rename as follows:

mv {,new.}original.filename

as this expands to:

mv original.filename new.original.filename
Genevagenevan answered 16/10, 2008 at 12:3 Comment(8)
Thanks, this looks to be the best solution - also works with ksh.Niobium
What this does not do is work with wild cards. That is, in a directory containing filenames a, b, c, using "echo {,new.}*" yields the list "a b c new.*" (because file name generation via brace expansion occurs before expanding wild card characters - at least by inference).Titmouse
Since months I'm looking for such a solution only using basic shell functionality... I would have never come up with something that simple, thanks a lot!Kitchener
Thanks for the reference to Brace Expansion :-) exactly what I was looking for!Debouchment
I love the example. I'm feeling in the mood to go to Baskin Robbins during my lunchbreak now.Chary
Serious question though - what .bashrc line would you need to get tab completion to expand it, so you can see what you are "really" going to execute.Chary
@JonathanLeffler You have to use bash scripting to act on multiple files, see Simon's answerKelter
I'm not a fan of tricks like this unless you use things like this regularly. The number of times I need to use this is small so I rather have a more straight forward solution that I have more of a chance of remembering.Feingold
C
201

You could use the rename(1) command:

rename 's/(.*)$/new.$1/' original.filename

Edit: If rename isn't available and you have to rename more than one file, shell scripting can really be short and simple for this. For example, to rename all *.jpg to prefix_*.jpg in the current directory:

for filename in *.jpg; do mv "$filename" "prefix_${filename}"; done;

or also, leveraging from Dave Webb's answer and using brace expansion:

for filename in *.jpg; do mv {,prefix_}"$filename"; done;
Chancellorship answered 16/10, 2008 at 11:48 Comment(8)
Installed graphite this weekend, and they have a step that's essentially ("copy all the *.example files into the conf folder, removing the example suffix"). Real lazy on their part, but rename 's/(.*).example/$1/' *.example saved me from the tedium.Content
This is the best answer so far.Garygarza
I don't think you need the first $ in this scenario. So just 's/(.*)/new.$1/'Pedicular
rename command does not work on my RHEL 6 machine. Are there any different versions of this command? The for ... done worked for me.Teredo
For those looking for the suffix version: for filename in *.jpg; do mv "$filename" "${filename}_suffix"; done;Cocoon
I recommend running for filename in *.jpg; do echo "prefix_${filename}"; done; first as a dry run to see that you get what you expectFeingold
@Cocoon that won't work you will get [filename].jpg_suffixFeingold
The single quotes on rename command are important to avoid shell expansionErlin
G
145

In Bash and zsh you can do this with Brace Expansion. This simply expands a list of items in braces. For example:

# echo {vanilla,chocolate,strawberry}-ice-cream
vanilla-ice-cream chocolate-ice-cream strawberry-ice-cream

So you can do your rename as follows:

mv {,new.}original.filename

as this expands to:

mv original.filename new.original.filename
Genevagenevan answered 16/10, 2008 at 12:3 Comment(8)
Thanks, this looks to be the best solution - also works with ksh.Niobium
What this does not do is work with wild cards. That is, in a directory containing filenames a, b, c, using "echo {,new.}*" yields the list "a b c new.*" (because file name generation via brace expansion occurs before expanding wild card characters - at least by inference).Titmouse
Since months I'm looking for such a solution only using basic shell functionality... I would have never come up with something that simple, thanks a lot!Kitchener
Thanks for the reference to Brace Expansion :-) exactly what I was looking for!Debouchment
I love the example. I'm feeling in the mood to go to Baskin Robbins during my lunchbreak now.Chary
Serious question though - what .bashrc line would you need to get tab completion to expand it, so you can see what you are "really" going to execute.Chary
@JonathanLeffler You have to use bash scripting to act on multiple files, see Simon's answerKelter
I'm not a fan of tricks like this unless you use things like this regularly. The number of times I need to use this is small so I rather have a more straight forward solution that I have more of a chance of remembering.Feingold
S
26

You can achieve a unix compatible multiple file rename (using wildcards) by creating a for loop:

for file in *; do
  mv $file new.${file%%}
done
Saucy answered 18/11, 2012 at 11:56 Comment(4)
What's the difference between this and what Simon Lehmann already answered?Niobium
This works great. What doe ${file%%} do? Seem similar to simply ${file}Dele
I'm curious about ${file%%} as wellSibeal
The %% looks like a mistake here to meJolda
T
10

I've seen people mention a rename command, but it is not routinely available on Unix systems (as opposed to Linux systems, say, or Cygwin - on both of which, rename is an executable rather than a script). That version of rename has a fairly limited functionality:

rename from to file ...

It replaces the from part of the file names with the to, and the example given in the man page is:

rename foo foo0 foo? foo??

This renames foo1 to foo01, and foo10 to foo010, etc.

I use a Perl script called rename, which I originally dug out from the first edition Camel book, circa 1992, and then extended, to rename files.

#!/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";
    }
}

This allows you to write any Perl substitute or transliterate command to map file names. In the specific example requested, you'd use:

rename 's/^/new./' original.filename
Titmouse answered 16/10, 2008 at 12:42 Comment(4)
Thanks, if I could accept a second answer this would be it. I wanted a standard command as something that will just work on anything I might use, but I can put this script on the machines I do use frequently and it will still be helpful.Niobium
James Wong notes that for Arch Linux, you could just download and install this script via pacman using: pacman -Sy perl-rename. Then add an alias to it in your bashrc: alias rename='perl-rename'Titmouse
what if "from" or "to" are empty strings, i.e. you want to append something to each filename or remove something completelySoothfast
@Soothfast — to append '.html to the file names, use 's/$/.html/' — to remove tmp from everywhere in the file names, use 's/tmp//g' (find tmp and replace with nothing everywhere).Titmouse
S
9

I know there is great answers here but I found no reference to handle filename extensions when adding suffix.

I needed to add '_en' suffix to all wav files in a folder before the file extension.

The magic is here: %.*

for filename in *.wav; do mv $filename ${filename%.*}_en.wav; done;

If you need to handle different file extensions, check this answer. A bit less intuitive.

Sharrisharron answered 22/6, 2020 at 12:15 Comment(0)
M
8

The easiest way to bulk rename files in directory is:

ls | xargs -I fileName mv fileName fileName.suffix
Mata answered 23/11, 2018 at 15:26 Comment(0)
W
2

If it's open to a modification, you could use a suffix instead of a prefix. Then you could use tab-completion to get the original filename and add the suffix.

Otherwise, no this isn't something that is supported by the mv command. A simple shell script could cope though.

Whitewall answered 16/10, 2008 at 11:41 Comment(1)
Often I want both, but primarily the prefix - as you say, tab completion can handle suffixes.Niobium
L
2

I don't like any of the other answers.
My take is that you want a vanilla bash solution. So here you go!

Try this with echo first to see what is going to happen!
Test your command before you use it on thousands of files!

Remove the './' if you want to put in a prefix
find . -type f -exec bash -c 'echo prefix_${0#./}' {} \;

In the question asked, prefix_ is new. so the command becomes
find . -type f -exec bash -c 'echo mv $0 new.${0#./}' {} \;

Remove the extension if you want to put something after it
find . -type f -exec bash -c 'echo ${0%.*}_suffix.${0##*.}' {} \;

Now to use this to rename just replace echo with mv $0.

EXAMPLE:
find . -type f -exec bash -c 'mv $0 1.${0#./}' {} \;

Inspired by: https://gist.github.com/larshaendler/723d2ec447c3c694b71dc6e3bc3aadc9

Logos answered 3/4, 2021 at 10:11 Comment(1)
This answer has been helpful to me. A note: for the prefix example you run the command for ALL files, and don't check that only intended files are affected. What I mean is that you might want to add an extra check for -name 'original*', like so: find . -type f -name 'original*' -exec bash -c 'echo mv $0 new.${0#./}' {} \;.Stamey
D
1

In my case I have a group of files which needs to be renamed before I can work with them. Each file has its own role in group and has its own pattern.

As result I have a list of rename commands like this:

f=`ls *canctn[0-9]*`   ;  mv $f  CNLC.$f
f=`ls *acustb[0-9]*`   ;  mv $f  CATB.$f
f=`ls *accusgtb[0-9]*` ;  mv $f  CATB.$f
f=`ls *acus[0-9]*`     ;  mv $f  CAUS.$f

Try this also :

f=MyFileName; mv $f {pref1,pref2}$f{suf1,suf2}

This will produce all combinations with prefixes and suffixes:

pref1.MyFileName.suf1
...
pref2.MyFileName.suf2

Another way to solve same problem is to create mapping array and add corespondent prefix for each file type as shown below:

#!/bin/bash
unset masks
typeset -A masks
masks[ip[0-9]]=ip
masks[iaf_usg[0-9]]=ip_usg
masks[ipusg[0-9]]=ip_usg
...
for fileMask in ${!masks[*]}; 
do  
registryEntry="${masks[$fileMask]}";
fileName=*${fileMask}*
[ -e ${fileName} ] &&  mv ${fileName} ${registryEntry}.${fileName}  
done
Dost answered 26/1, 2016 at 23:12 Comment(2)
As far as I can tell f=`ls *canctn[0-9]*` ; mv $f CNLC.$f only works if it matches a single fileFeingold
thats true. to avoid that you can use find <code> find * -maxdepth 1 -type f -name "canctn[0-9]" -exec echo mv {} CNLC.$(basename {}) \; </code> But this because too big for a compact repetitive task.Dost
H
1

Bulk rename files bash script

#!/bin/bash
# USAGE: cd FILESDIRECTORY; RENAMERFILEPATH/MultipleFileRenamer.sh FILENAMEPREFIX INITNUMBER
# USAGE EXAMPLE: cd PHOTOS; /home/Desktop/MultipleFileRenamer.sh 2016_
# VERSION: 2016.03.05.
# COPYRIGHT: Harkály Gergő | mangoRDI (https://wwww.mangordi.com/) 

# check isset INITNUMBER argument, if not, set 1 | INITNUMBER is the first number after renaming
if [ -z "$2" ]
    then i=1;
else
    i=$2;
fi

# counts the files to set leading zeros before number | max 1000 files
count=$(ls -l * | wc -l)
if [ $count -lt 10 ]
    then zeros=1;
else
    if [ $count -lt 100 ]
        then zeros=2;
    else
        zeros=3
    fi
fi

# rename script
for file in *
do
    mv $file $1_$(printf %0"$zeros"d.%s ${i%.*} ${file##*.})
    let i="$i+1"
done
Hooke answered 5/3, 2016 at 22:51 Comment(0)
F
0

This expands a bit on the accepted answer here and combines it with another answer from Unix.SE.

This is designed as a way to get more power whilst relying on the simplest of commands and still a one-liner.

Add a prefix

Dry-run command ;)

for old in *.jpg; do new=`echo $old | sed -e's/^/prefix_/'`; echo "$old -- $new"; done

For real:

for old in *.jpg; do new=`echo $old | sed -e's/^/prefix_/'`; mv "$old" "$new"; done

This uses sed search and replace to replace the start of the string ^ with prefix_.

Add a suffix

This is useful because you get the additional power of sed so that you can use the same pattern to add a suffix:

# \1 might be $1 for you
for old in *.jpg; do new=`echo $old | sed -e's/(.jpg)$/_suffix\1/'`; mv "$old" "$new"; done

If you're less familiar with sed, this is equivalent to:

for old in *.jpg; do new=`echo $old | sed -e's/.jpg$/_suffix.jpg/'`; mv "$old" "$new"; done

Remove a prefix/suffix

Now should you mess up with the previous commands you can remove a prefix/suffix:

for old in *.jpg; do new=`echo $old | sed -e's/^prefix_//'`; mv "$old" "$new"; done
Feingold answered 9/3, 2023 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.