开发者

Search+replace strings in filenames

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 with 'bar'?

For example, if the current structure looks like:

-foo开发者_StackOverflow社区_test
    - fooo.txt
    - xfoo
        - yfoo.h
- 1foo.c

It should look like this after running the bash script:

-bar_test
    - baro.txt
    - xbar
        - ybar.h
- 1bar.c


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.


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜