Super User Asked on December 5, 2021
How do I rename all files in a directory, recursively, changing one file extension to another, for thousands of files in thousands of subfolders? I see a lot of commands that do almost what I want, but not quite.
find . -name "*.andnav" -exec rename .andnav .tile {} ;
syntax error at (eval 1) line 1, near "."
…
rename -nv 's/.andnav$/.tile/i' *.andnav
0.png.andnav renamed as 0.png.tile
I have started to put together a tool to provide a simplified interface to common actions.
You can recursively rename files with glob pattern matching and regex matching like this:
$ npm install @mountbuild/mouse -g
$ mouse rename file -p "tmp/**/*.jpg" -i ".jpg" -o ".png"
If nothing else check out the source and see how to write your own script to do this in JavaScript.
Answered by Lance Pollard on December 5, 2021
If you have zsh shell, this is satisfaction:
rename -n -e 'EXPRESSION_HERE' **/*
Remove -n
to work.
Answered by Ivan Gonzalez on December 5, 2021
find -execdir rename
https://superuser.com/a/213146/128124 works directly only for suffixes, but this will work for arbitrary regex replacements on basenames:
PATH=/usr/bin find . -depth -execdir rename 's/_dbg.txt$/_.txt' '{}' ;
or to affect files only:
PATH=/usr/bin find . -type f -execdir rename 's/_dbg.txt$/_.txt' '{}' ;
-execdir
first cd
s into the directory before executing only on the basename.
Tested on Ubuntu 20.04, find 4.7.0, rename 1.10.
Convenient and safer helper for it
find-rename-regex() (
set -eu
find_and_replace="$1"
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^/][^:]*//g')"
find . -depth -execdir rename "${2:--n}" "s/${find_and_replace}" '{}' ;
)
Sample usage to replace spaces ' ' with hyphens '-'.
Dry run that shows what would be renamed to what without actually doing it:
find-rename-regex ' /-/g'
Do the replace:
find-rename-regex ' /-/g' -v
Command explanation
The awesome -execdir
option does a cd
into the directory before executing the rename
command, unlike -exec
.
-depth
ensure that the renaming happens first on children, and then on parents, to prevent potential problems with missing parent directories.
-execdir
is required because rename does not play well with non-basename input paths, e.g. the following fails:
rename 's/findme/replaceme/g' acc/acc
The PATH
hacking is required because -execdir
has one very annoying drawback: find
is extremely opinionated and refuses to do anything with -execdir
if you have any relative paths in your PATH
environment variable, e.g. ./node_modules/.bin
, failing with:
find: The relative path ‘./node_modules/.bin’ is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove that entry from $PATH
-execdir
is a GNU find extension to POSIX. rename
is Perl based and comes from the rename
package.
Rename lookahead workaround
If your input paths don't come from find
, or if you've had enough of the relative path annoyance, we can use some Perl lookahead to safely rename directories as in:
git ls-files | sort -r | xargs rename 's/findme(?!.*/)/?$/replaceme/g' '{}'
I haven't found a convenient analogue for -execdir
with xargs
: Xargs: change working directory to file path before executing?
The sort -r
is required to ensure that files come after their respective directories, since longer paths come after shorter ones with the same prefix.
Tested in Ubuntu 18.10.
Answered by Ciro Santilli 新疆再教育营六四事件法轮功郝海东 on December 5, 2021
I found this method is easier and easier to read:
find . -name "*.andnav" | rename "s/.andnav$/.tile/"
At least on Ubuntu derivations rename takes a list of files from STDIN if none are on the command line. And this can be tested easily with:
find . -name "*.andnav" | rename -vn "s/.andnav$/.tile/"
until you get it right.
Answered by Bernd Wechner on December 5, 2021
With zsh:
autoload zmv
zmv -n '(**/)(*).andnav' '$1$2.tile'
Remove the -n
to actually perform the renaming.
Answered by Gilles 'SO- stop being evil' on December 5, 2021
Figured it out
find . -name "*.andnav" -exec rename -v 's/.andnav$/.tile/i' {} ;
./0/0.png.andnav renamed as ./0/0.png.tile
./0/1.png.andnav renamed as ./0/1.png.tile
./1/0.png.andnav renamed as ./1/0.png.tile
./1/1.png.andnav renamed as ./1/1.png.tile
of course remove the -v when actually doing it, or it will waste time displaying all the files
Answered by endolith on December 5, 2021
Something like:
find . -name '*.andnav' -exec sh -c 'mv "$0" "${0%.andnav}.tile"' {} ;
The above starts walking the directory tree starting at the current working directory (.
). Every time a file name matches the pattern *.andnav
(e.g., foo.andnav
) the following command is executed:
sh -c 'mv "$0" "${0%.andnav}.tile"' foo.andnav
Where $0
is foo.andnav
and ${0%.andnav}.tile
replaces the .andnav
suffix with .tile
so basically:
mv foo.andnav foo.tile
Answered by cYrus on December 5, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP