I use this command to rename files recursively:
find -iname \*.bak | rename 's/.bak/.old/'
But I want to exclude the current directory. Example:
.bak
dir1/.bak
dir2/.bak
...
After I want this:
.bak
dir1/.old
dir2/.old
...
I use this command to rename files recursively:
find -iname \*.bak | rename 's/.bak/.old/'
But I want to exclude the current directory. Example:
.bak
dir1/.bak
dir2/.bak
...
After I want this:
.bak
dir1/.old
dir2/.old
...
With a find
that supports the -mindepth
predicate:
find . -mindepth 2 -type f -name '*.bak' | rename 's/\.bak$/.old/'
The -mindepth
predicate allows you to define the minimum search depth at which matches can be made. Depth zero is the root search path itself (.
), depth 1 is anything immediately under the root search path (e.g. ./somename
), and depth 2 is anything immediately below that (e.g. ./somename/other
), etc.
Note that the caseinsensitive match that -iname
does is not needed as your rename
would not rename files that have other types of capitalization of the filename suffix anyway, so I've changed it to a -name
test instead. I've also fixed your rename
call so that files with the string bak
elsewhere in the name, like rebak.bak
, is not rename in strange ways (e.g. into r.old.bak
).
I've also added -type f
to only make find
consider regular files, i.e. not directories etc.
For a more portable solution:
find . -mindepth 2 -type f -name '*.bak' -exec sh -c '
for pathname do
mv "$pathname" "${pathname%.bak}.old"
done' sh {} +
This does not rely on the rename
utilitiy.
find . -mindepth 2 -type f -name '*.bak' -exec rename 's/\.bak$/.old/' {} +
This calls rename
directly from find
.
Related:
With bash
, you can skip find
altogether and instead do
shopt -s globstar dotglob
rename 's/\.bak$/.old/' ./*/**/*.bak
The **
globbing pattern matches any number of intermediate subdirectories.
This special pattern is enabled with the globstar
shell option. The initial ./*/
makes sure that we only look in subdirectories of the current directory, and not in the current directory itself.
The dotglob
shell option makes sure that we match hidden names with the pattern.
The above would however not pick out regular files exclusively (but neither does your find
command). To do that, use a loop:
shopt -s globstar dotglob
for pathname in ./*/**/*.bak; do
[[ ! -f $pathname ]] && continue
rename 's/\.bak$/.old/' "$pathname"
done
This would also rename symbolic links to regular files.
./.bak
, but not e.g../somefile.bak
. – Kusalananda Feb 10 '20 at 08:07