Adventures in Shell Scripting

Brad R Friday 21 August 2009 - 15:05:17  

Today I needed to do something different. I needed to copy all the files with the same DOS filename, to a new DOS filename, preserving the DOS file extensions. That is, I wanted to do

cp oldname.doc newname.doc
cp oldname.txt newname.txt
cp oldname.ods newname.ods
...

for all the different extensions of "oldname"...and I don't know in advance what those extensions are. Essentially, what I wanted to do was

cp oldname.* newname.*

except that that command will not work, in DOS or Linux.* But I was sure that this could be done in Linux, with a shell script. (Feel free to skip the rest if this subject is not interesting to you.)

* Update: A visitor, Dan, writes in to tell me that that command will work in the Windows shell, and also works in MS-DOS 6.22. I wasn't sure about Windows; but DOS was my mistake.
It took perhaps an hour or so of digging through my shell scripting book, and searching with Google, before I found the clue in this forum thread. That didn't give me exactly what I needed, but it pointed me close enough to the final solution.

#!/bin/bash
# copy all extensions of a given file name to a new file name
# Usage:  copyext oldname newname

for i in $1.*
do
  cp "$i" "$2${i#$1}"
done


When invoked with "copyext oldname newname", $1 will be replaced with oldname and $2 will be replaced with newname.

The for...do...done construct will repeat a single command multiple times. In this case, it repeats for each i in $1.* -- this means that i will successively take on each file name of the form "oldname.*" (recall that $1 will be replaced with oldname when the command is run).

The first argument of the cp (copy) command is $i, which is just a file name that matches "oldname.*" -- for example, $i could be "oldname.txt".

The second argument uses a built-in substitution function (this is what took me all the research). The phrase ${i#$1} means take the string given by variable i (e.g. "oldname.txt"), and remove from the front of it the string given by $1 (i.e., "oldname"). So that phrase just returns the extension of the matching filename (".txt"). Then that is combined with $2 ("newname") to give the new filename and the matching extension.

I tested this by using echo instead of cp

for i in $1.*
do
  echo "$i" "$2${i#$1}"
done


which just prints "oldname.ext newname.ext" for each file extension.

I've added a few refinements -- such as checking that the correct number of arguments are present -- and added this to my toolbox. And now I have precisely the function I need!


printer friendly