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 <[email protected]>
_______________________________________________
luv-main mailing list
[email protected]
https://lists.luv.asn.au/cgi-bin/mailman/listinfo/luv-main