Using bash, how can I search for all occurrences of the substring \'foo\' in all filenames (including folders) contained recursively in a directory and replace them them wit
This was tricky because of directory names with multiple instances of "foo". When you change ./foo_test/xfoo
to ./bar_test/xbar
everything that was in ./foo_test
becomes inaccessible. So I changed the file names first then changed the last occurrence of "foo" in the directory names. I added echo statements to track what's going on during development. You can, of course, expunge them.
#!/bin/sh
#first change the file names
#append '.' to process files in current directory
for D in $(find -d . -name "*foo*" -type d ) '.'
do
pushd $D >> /dev/null
echo 'directory: ' "$D"
for file in $(find . -name "*foo*" -type f -maxdepth 1)
do
echo ' change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
mv "$file" `echo "$file" | sed s/foo/bar/g`
done
popd >> /dev/null
done
echo ''
#Now change the directory names
for D in $(find -d . -name "*foo*" -type d )
do
echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
#change only the last occurance of foo
mv "$D" `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
done
I have no doubt there are shorter, more elegant ways to do this (probably just by removing half of the lines in this script), but I'm pretty sure this works.
EDIT
The identical loops were a red flag. This version only loops once. You get a message attempting mv '.' '.'
, but it's safely ignored.
#!/bin/sh
#first change the file names
#append '.' to change file in current directory
for D in $(find -d . -name "*foo*" -type d ) '.'
do
pushd $D >> /dev/null
echo 'directory: ' "$D"
for file in $(find . -name "*foo*" -type f -maxdepth 1)
do
echo ' change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
mv "$file" `echo "$file" | sed s/foo/bar/g`
done
popd >> /dev/null
echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
#change only the last occurence of foo
mv "$D" `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
done
Both variations shown here using work correctly on OPs test structure:
find . -depth -name '*foo*' -execdir bash -c 'mv -i "$1" "${1//foo/bar}"' bash {} \;
or, if you have a very large number of files and want it to run faster:
find . -depth -name '*foo*' -execdir bash -c 'for f; do mv -i "$f" "${f//foo/bar}"; done' bash {} +
EDIT: As noted in the comments, my earlier answer using a find
command that did not use the execdir
option and using rename
has problems renaming files in directories that contain foo in their name. As suggested, I have changed the find commands to use -execdir
, and I have deleted the variation using the rename
command since it is a non-standard command.