I hate not being able to rename multiple files from the command prompt/mac terminal session, ie:
mv *.thisisthewrongextension *.thisiscorrector as my example will be for torrent files incorrectly downloaded by a browser as .torrent.html files:
mv *.torrent.html *.torrentThis lack in functionality in bash just drives me crazy.
Solution:
Here are two inline commands, a bash script, a pearl script, and GUI Mac program, that will overcome this shortfall. In these two command examples we will try to rename files that downloaded as myfile.torrent.html to myfile.torrent.
find *.torrent.html -exec mv -vn {} `echo {} | cut -f1 -d.`.torrent \;for i in *.torrent.html; do mv -vn "$i" "`basename $i .torrent.html`.torrent"; done
bash/pearl script examples located here
You could also use this GUI program on a mac, it is VERY, VERY, VERY awesome: A Better Finder Rename
find *.torrent.htmlwill output:
myfile1.torrent.htmlThen we run into the "-exec" parameter of the find command... it may seam a lot to deal wth, but basically it just runs a command each time find returns a result. Moreover it will pass the found file/directory in the form of "{}". for example:
myfile2.torrent.html
myfile3.torrent.html
find *.torrent.html -exec echo {} \;
Please note: at the end of an "exec" you must have whitespace(a space in the above example), then a backslash(escape character) followed by a semicolon. If you don't do that you'll get the following error:
anyway, the aforementioned command will output:find: -exec: no terminating ";" or "+"
myfile1.torrent.htmlLet's take a break from the "exec" function of the "find" command, and move on to the "mv" command (pun intended). When you move or rename a file you need to specify a source & destination file/directory. Now since we can't move multiple files with a wildcard as the destination with the "mv" command (as is the point of this article) will not work
myfile2.torrent.html
myfile3.torrent.html
mv *.torrent.html *.torrent - this will not workHowever, the following 3 commands will work (and this is exactly what we will be replicating in this first example):
mv myfile1.torrent.html myfile1.torrentI'm also adding the "-vn" options to the "mv" command so it will be verbose about what it is moving & to prevent "mv" from overwriting an existing file. Now that we know what "mv" will be expecting, let's see how we can get "find" & "-exec" to pass the file names to the "mv" command.
mv myfile2.torrent.html myfile2.torrent
mv myfile3.torrent.html myfile3.torrent
find *.torrent.html -exec mv -vn {} `echo {} | cut -f1 -d.`.torrent \;
find *.torrent.html -exec bash -c 'mv -vn {} $(basename {} .torrent.html).torrent' \;
The {} portion of the above command will be replaced with "myfile1.torrent.html". For the next portion of the command we will look at the bold section in the command above.
`echo {} | cut -f1 -d.` is translated into:
`echo myfile1.torrent.html | cut -f1 -d.`
$(basename {} .torrent.html) is translated to:The above example will give the following output (note that anything inside the ` (tick marks) will be executed first):
$(basename myfile1.torrent.html .torrent.html)
myfile1This output still needs an extension appended to it, so we do the following:
`echo {} | cut -f1 -d.`.torrent
$(basename {} .torrent.html).torrentAnd we get the following output:
myfile1.torrentSo now the "mv" command has both a source ("{}" as represented in the example below), and the destination (
find *.torrent.html -exec mv -vn {} `echo {} | cut -f1 -d.`.torrent \;
find *.torrent.html -exec bash -c 'mv -vn {} $(basename {} .torrent.html).torrent' \;With the following output:
myfile1.torrent.html -> myfile1.torrentPhew... all done with the first example.
myfile2.torrent.html -> myfile2.torrent
myfile3.torrent.html -> myfile3.torrent
Solution 2 breakdown... sort of:
Now let's dissect the single line bash script:
for i in *.torrent.html; do mv -vn "$i" "`basename $i .torrent.html`.torrent"; doneExpanded below with added comments it looks like:
for i in *.torrent.html; #(the for loop begins here)I could go into more details, but I think that should be enough for that today. For more details about this function, and to see some other bash function examples go here.
do
mv -vn "$i" "`basename $i .torrent.html`.torrent"; #(here we use the mv command, as we saw above, but we use the "basename" function to split the file name)
done #(and then we are done)
If you need help with a bash script:
- Feel free to comment/mail me
- Visit a linux forum (www.linuxquestions.org)
- Take a good long look at the Advanced Bash Scripting Guide
- Use the "man" pages for further detailed info.
=:~)