Batch rename in Bash
There are a few tools that provide batch renaming for the shell, but most of them are quite huge and need installing. It is not necessary to use these utilities, because unix already all tools necessary:
$ ls -d *.txt | sed 'p;s/foo/bar/' | xargs -l2 mv
The first part should be quit clear: Print all files ending with
\*.txt makes use of the shell's globbing features
and filters all the
-d switch tells
ls not to print the contents of directories.
The sed expression consists of two parts:
the current line and
s/foo/bar/ is the actual transformation
(in this case: a replacement).
If I run this on my home directory I get this:
$ ls down duh files fuu fuubar pr0j tmp usr $ ls | sed 'p;s/fuu/bar/' down down duh duh files files fuu bar fuubar barbar pr0j pr0j tmp tmp usr usr
Notice that most files have just been printed twice,
fuubar where changed to
Now comes the tricky bit:
xargs takes each two lines
and applies them to
mv as arguments,
so when I run xargs in debug mode I get this:
$ ls | sed 'p;s/fuu/bar/' | xargs -l2 echo mv mv down down mv duh duh mv files files mv fuu bar mv fuubar barbar mv pr0j pr0j mv tmp tmp mv usr usr
Notice that I did not use the
'-d' flag this time,
because I print the content of the directory
'.' this time,
not a list of given files.
This would happen if I did use
$ ls -d . $ ls | sed 'p;s/fuu/bar/' | xargs -l2 echo mv mv . .
In the above example I did not do any filtering,
because the files
(which existed before I began to write this article)
do not have an extension, but I could use a filter to select only those files I
actually want to rename:
$ ls -d *fuu* | sed 'p;s/fuu/bar/' | xargs -l2 echo mv mv fuu bar mv fuubar barbar
I can get as elaborate as I want with my filter if I use grep; here is the same as above using grep:
$ ls | grep 'fuu' | sed 'p;s/fuu/bar/' | xargs -l2 echo mv mv fuu bar mv fuubar barbar
Instad of using xargs you can also use a while loop; I use that variant to save me the trouble of dealing with escaping in xargs:
$ ls | grep 'fuu' | sed 'p;s/fuu/bar/' | while read a && read b; do echo mv "$a" "$b"; done mv fuu bar mv fuubar barbar
One last example, where we replace files recursively in the home directory:
find instead of
ls which lists a directory recursively
(I am not actually running this and neither should you):
$ find | sed 'p;s/fuu/bar/' | xargs -l2 mv ...
CommentsWrite a comment.