rename multiple files on the command line
http://www.basicallytech.com/blog/?/archives/10-Shell-stuff-rename-multiple-files-on-the-command-line.html#remove_spaces_tr
If you wish to quickly rename multiple files in a directory, a for loop
(sometimes combined with other utilities such as sed or tr) is one way to
do the job.
The examples in this article include removing spaces from filenames,
adding and removing suffixes and prefixes, and changing from uppercase to
lowercase.
Here is a full list of the examples I'm going to look at:
remove spaces from file names
alternative way to remove spaces from file names
add a suffix
add a prefix
remove a prefix
remove a suffix
uppercase to lowercase
uppercase to lowercase (suffix only)
an unlikely situation
remove spaces from file names
I've got some imaginary mp3 files with spaces in the file names. Spaces in
filenames are a bad idea. It's inconvenient, spaces are the delimiters
between command-line arguments, and for files you're putting out over the
Internet, it can cause other problems, so we're going to get rid of these
spaces as a matter of principle. (Whose foolish idea was it to put spaces
into filenames, anyway?)
$ ls -l
total 468
-rw-r--r-- 1 rob rob 15015 2006-10-20 11:18 01 - Some Song.mp3
-rw-r--r-- 1 rob rob 17313 2006-10-20 11:18 02 - Another Song.mp3
-rw-r--r-- 1 rob rob 17381 2006-10-20 11:19 03 - Yet Another Song.mp3
-rw-r--r-- 1 rob rob 410075 2006-10-20 11:19 04 - A New Song.mp3
-rw-r--r-- 1 rob rob 7327 2006-10-20 11:19 05 - Some Instrumental
Piece.mp3
I'm going to change the spaces to underscores. Since I don't want to mess
things up, I'll be cautious to begin with:
$ for FILE in *.mp3 ; do NEWFILE=`echo $FILE | sed 's/ /_/g'` ; echo
"$FILE will be renamed as $NEWFILE" ; done
01 - Some Song.mp3 will be renamed as 01_-_Some_Song.mp3
02 - Another Song.mp3 will be renamed as 02_-_Another_Song.mp3
03 - Yet Another Song.mp3 will be renamed as 03_-_Yet_Another_Song.mp3
04 - A New Song.mp3 will be renamed as 04_-_A_New_Song.mp3
05 - Some Instrumental Piece.mp3 will be renamed as
05_-_Some_Instrumental_Piece.mp3
That looks okay, so I'll try it for real.
$ for FILE in *.mp3 ; do NEWFILE=`echo $FILE | sed 's/ /_/g'` ; mv "$FILE"
$NEWFILE ; done
$ ls -l
total 468
-rw-r--r-- 1 rob rob 15015 2006-10-20 11:18 01_-_Some_Song.mp3
-rw-r--r-- 1 rob rob 17313 2006-10-20 11:18 02_-_Another_Song.mp3
-rw-r--r-- 1 rob rob 17381 2006-10-20 11:19 03_-_Yet_Another_Song.mp3
-rw-r--r-- 1 rob rob 410075 2006-10-20 11:19 04_-_A_New_Song.mp3
-rw-r--r-- 1 rob rob 7327 2006-10-20 11:19
05_-_Some_Instrumental_Piece.mp3
I realise that there is an alternative way, using tr, which is easier (to
type ). However I would argue that the principle of the above technique
(using sed) can be applied to more situations, making it more useful as a
template.
For those who are unfamiliar with the command line, I'm going to break
this down into it's constituent components. If you are familiar with this
sort of thing, feel free to skip down to the other examples.
This is not the place to discuss for loops in great detail. There are
loads of resources on the Internet for that sort of thing. When I was
starting to learn this stuff, I always found detailed analysis of these
things confusing, and that the best way to learn was to actually do it.
for FILE in *.mp3
In this part of the loop, FILE is the variable, and *.mp3 is the argument.
The for loop generates a variable for each argument until there are no
arguments left. In the example above, the variables generated are all the
files which match the regular expression *.mp3, in other words:
01 - Some Song.mp3
02 - Another Song.mp3
03 - Yet Another Song.mp3
04 - A New Song.mp3
05 - Some Instrumental Piece.mp3
; do
The semicolon is special character which allows you end one command and
run another command on the same line. Every for requires a do.
NEWFILE=`echo $FILE | sed 's/ /_/g'`
Now I'm creating a new variable called NEWFILE. sed is used to replace
each space in the name of the mp3 file referenced by FILE with an
underscore (s/ /_/g), so the variable NEWFILE is the same as FILE, except
the value of each variable has every space replaced with an underscore.
The next bit is the command we want to run on each argument:
mv "$FILE" $NEWFILE
Note that I put the loop-generated $FILE variable inside double-quote
marks for the mv command. This is required because of those spaces in the
original file name. If you don't do that the loop will try to run the
following command on each file:
mv 01 - Some Song.mp3 01_-_Some_Song.mp3
instead of
mv "01 - Some Song.mp3" 01_-_Some_Song.mp3
; done
Finally, every do requires a done.
Here are some other examples.
alternative way to remove spaces from file names
$ ls -l
total 468
-rw-r--r-- 1 rob rob 15015 2006-10-20 11:18 01 - Some Song.mp3
-rw-r--r-- 1 rob rob 17313 2006-10-20 11:18 02 - Another Song.mp3
-rw-r--r-- 1 rob rob 17381 2006-10-20 11:19 03 - Yet Another Song.mp3
-rw-r--r-- 1 rob rob 410075 2006-10-20 11:19 04 - A New Song.mp3
-rw-r--r-- 1 rob rob 7327 2006-10-20 11:19 05 - Some Instrumental
Piece.mp3
$ for FILE in *.mp3 ; do mv "$FILE" `echo $FILE | tr ' ' '_'` ; done
$ ls -l
total 468
-rw-r--r-- 1 rob rob 15015 2006-10-20 11:18 01_-_Some_Song.mp3
-rw-r--r-- 1 rob rob 17313 2006-10-20 11:18 02_-_Another_Song.mp3
-rw-r--r-- 1 rob rob 17381 2006-10-20 11:19 03_-_Yet_Another_Song.mp3
-rw-r--r-- 1 rob rob 410075 2006-10-20 11:19 04_-_A_New_Song.mp3
-rw-r--r-- 1 rob rob 7327 2006-10-20 11:19
05_-_Some_Instrumental_Piece.mp3
add a suffix to multiple files
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices
In this example, I wish to add a .txt suffix to each file in this
directory.
We don't need sed here:
$ for FILE in * ; do mv $FILE $FILE.txt ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices.txt
add a prefix
Taking the files from the previous example:
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices.txt
I wish to add a maths_ prefix to each file:
$ for FILE in * ; do mv $FILE maths_$FILE ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 maths_algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 maths_calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 maths_equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 maths_geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 maths_matrices.txt
remove a prefix
Sticking with the files we've been using:
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 maths_algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 maths_calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 maths_equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 maths_geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 maths_matrices.txt
I've changed my mind about that maths_ prefix and want to remove it.
$ for FILE in * ; do NEWFILE=`echo $FILE | sed 's/^maths_//'` ; mv $FILE
$NEWFILE ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices.txt
The ^ symbol in sed matches the start of a line.
remove a suffix
While I'm at it, I think I'll remove the suffix as well.
$ for FILE in *.txt ; do NEWFILE=`echo $FILE | sed 's/.txt$//'` ; mv $FILE
$NEWFILE ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices
The $ symbol in sed matches the end of a line.
uppercase to lowercase
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 ALGEBRA
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 CALCULUS
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 EQUATIONS
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 GEOMETRY
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 MATRICES
$ for FILE in * ; do mv $FILE `echo $FILE | tr '[A-Z]' '[a-z]'` ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices
uppercase to lowercase (suffix only)
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 Algebra.TXT
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 Calculus.TXT
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 Equations.TXT
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 Geometry.TXT
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 Matrices.TXT
$ for FILE in *.TXT ; do NEWFILE=`echo $FILE | sed 's/.TXT$/.txt/'` ; mv
$FILE $NEWFILE ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 Algebra.txt
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 Calculus.txt
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 Equations.txt
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 Geometry.txt
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 Matrices.txt
an unlikely situation
Finally, an unlikely situation. I have a bunch of files with multiple
spaces and capitalised suffixes. I want the spaces changed to an
underscore, except where there are two or more consecutive spaces, where I
want just a single underscore. While we're at it, if a space is next to
the dot of a prefix, let's just remove it. I also want to make the
suffixes lowercase. Hmm.
$ ls -l
total 20
-rw-r--r-- 1 rob rob 201 2006-10-23 23:44 A file with many spaces.TXT
-rw-r--r-- 1 rob rob 1579 2006-10-23 23:44 Another wierdly named file.TXT
-rw-r--r-- 1 rob rob 1452 2006-10-23 23:44 My mate Arron spels beter than
this.txt
-rw-r--r-- 1 rob rob 924 2006-10-23 23:44 Not really.TXT
-rw-r--r-- 1 rob rob 379 2006-10-23 23:44 Whoo named this .TXT
I'm not going to try to correct the spelling as well!
$ for FILE in * ; do NEWFILE=`echo $FILE | sed -e 's/.TXT$/.txt/' -e 's/[
]*[ ]/_/g' -e 's/_[.]/./g'` ; mv "$FILE" $NEWFILE ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 201 2006-10-23 23:44 A_file_with_many_spaces.txt
-rw-r--r-- 1 rob rob 1579 2006-10-23 23:44 Another_wierdly_named_file.txt
-rw-r--r-- 1 rob rob 1452 2006-10-23 23:44
My_mate_Arron_spels_beter_than_this.txt
-rw-r--r-- 1 rob rob 924 2006-10-23 23:44 Not_really.txt
-rw-r--r-- 1 rob rob 379 2006-10-23 23:44 Whoo_named_this.txt
That was somewhat contrived, but it does provide an example of the power
of the command line.
Bear in mind that some people pay for this sort of functionality. Mind
you, consider their option.
addendum
It occurred to me after I wrote this article that some one might want to
emulate the windows option.
Giving all the files the same name, but with a different number seems,
well, foolish, but let's do it anyway. Parentheses are also a bad idea in
filenames, but this is just an exercise.
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices
$ NUM=0 ; for FILE in * ; do NUM=`expr $NUM + 1` ; mv $FILE
foolish\($NUM\) ; done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 foolish(1)
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 foolish(2)
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 foolish(3)
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 foolish(4)
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 foolish(5)
What might be more useful would be to retain the original name and
seperate it from the sequential number with a much more command-line
friendly underscore.
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices
$ NUM=0 ; for FILE in * ; do NUM=`expr $NUM + 1` ; mv $FILE ${FILE}_$NUM ;
done
$ ls -l
total 20
-rw-r--r-- 1 rob rob 733 2006-10-20 13:23 algebra_1
-rw-r--r-- 1 rob rob 194 2006-10-20 13:23 calculus_2
-rw-r--r-- 1 rob rob 117 2006-10-20 13:23 equations_3
-rw-r--r-- 1 rob rob 402 2006-10-20 13:23 geometry_4
-rw-r--r-- 1 rob rob 50 2006-10-20 13:23 matrices_5
Happy renaming.