Combining the find -exec bash
idea with the bash
loop idea, you can use the +
terminator on the -exec
to tell find
to pass multiple filenames to a single invocation of the bash command. Pass the new type as the first argument - which shows up in $0
and so is conveniently skipped by a for
loop over the rest of the command-line arguments - and you have a pretty efficient solution:
find . -type f -iname "*.$arg1" -exec bash -c \
'for arg; do mv "$arg" "${arg%.*}.$0"; done' "$arg2" {} +
Alternatively, if you have either version of the Linux rename
command, you can use that. The Perl one (a.k.a. prename
, installed by default on Ubuntu and other Debian-based distributions; also available for OS X from Homebrew via brew install rename
) can be used like this:
find . -type f -iname "*.$arg1" -exec rename 's/\Q'"$arg1"'\E$/'"$arg2"'/' {} +
That looks a bit ugly, but it's really just the s/old/new/
substitution command familiar from many UNIX tools. The \Q
and \E
around $arg1
keep any weird characters inside the suffix from being interpreted as regular expression metacharacters that might match something unexpected; the $
after the \E
makes sure the pattern only matches at the end of the filename.
The pattern-based version installed by default on Red Hat-based Linux distros (Fedora, CentOS, etc) is simpler:
find . -type f -iname "*.$arg1" -exec rename ".$arg1" ".$arg2" {} +
but it's also dumber: if you rename .com .exe stackoverflow.com_scanner.com
, you'll get a file named stackoverflow.exe_scanner.exe
.