Search+replace strings in filenames

前端 未结 2 1504
感动是毒
感动是毒 2020-12-13 19:32

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

相关标签:
2条回答
  • 2020-12-13 19:52

    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 讨论(0)
  • 2020-12-13 19:53

    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.

    0 讨论(0)
提交回复
热议问题