
On Fri, Nov 15, 2019 at 11:51:40AM +1000, Piers wrote:
I have a bunch of files that I want to rename:
123.someword.doc > 123.doc
456.someword.pdf > 456.pdf
The "someword" is consistent in all the files and they need to be renamed recursively.
Use the perl-based 'rename' tool (which is sometimes in the PATH as 'prename' or 'file-rename' instead of just 'rename'). Note, this is **NOT** the same as the rename tool from util-linux (which has completely different command-line options and capabilities, and is sometimes called 'rename.ul'). On debian (and ubuntu, etc), 'apt-get install rename' if it isn't already installed (run 'man rename' to see if it mentions Perl). On other distros, install whatever package provides the perl File::Rename module. Anyway, the perl 'rename' allows you to use ANY perl code, from trivial sed-like search and replace to complex perl code blocks to rename files. e.g. i've written rename commands that sort files into sub-directories by the file's timestamp or name, convert them to TitleCase, change spaces to '_' or '.' (or just remove them), AND change their permissions - all in one command. But most of the time, I just need to do a regexp search and replace on the filenames. Like this: rename -n 's/\.someword//' *someword* NOTE: The '-n' is a dry-run, it shows you what would be renamed if you allowed it. To actually rename, get rid of the '-n' or replace it with '-v' for verbose output. BTW, rename won't overwrite existing files unless you force it to with '-f'. Perl rename can take a list of filenames on the command-line or from stdin, so you could do a recursive rename with either of these: find . -type f -name '*someword*' -exec rename -n 's/\.someword//' {} + find . -type f -name '*someword*' -print0 | rename -n -0 's/\.someword//'
Something like this but with a different regex:
# This is for a completely different file name structure
find . -name '123*.txt' -type f -exec bash -c 'mv "$1" "${1/\/123_//}"' -- {} \;
You really don't want to be forking 'bash' AND 'mv' once for each matching file. The following line tries to fit as many filenames on the command line as will fit (the command line length limit is typically around 2MB these days), so will almost always only run bash once. or maybe twice. as few times as necessary, anyway. find . -name '123*.txt' -type f -exec bash -c 'for f in "$@"; do mv "$f" "${1/\/123_//}"; done' -- {} + Note the use of '{} +' instead of '{} \;' - that's what makes 'find' try to fit as many filenames onto a command-line as possible each time the -exec option's command is executed. Unfortunately, this still forks 'mv' once for each filename, so it's still going to be slow. Even better, don't use bash at all, use the right tool for the job: find . -type f -name '123*.txt' -exec rename -n 's:/123_::' {} + Or, to pipe a NUL separated list of filenames (to avoid command-line length limits entirely): find . -type f -name '123*.txt' -print0 | rename -n -0 's:/123_:/:' (again, remove the '-n' or replace with '-v' to make it actually rename files once you've verified that it does what you want). craig -- craig sanders <cas@taz.net.au>