Recursively rename files and directories

findrename

I'm using this command to rename files:

for fname in *;
do
    mv "$fname" $(echo "$fname" | sha1sum | cut -f1 -d' ')
done

But it only renames in the current directory. Let's say I have many directories, and each directory contains some other directories, and last directory tree contains files. I want to rename them with random characters.

I think find . -type f should work, and have tried it, but still did not get any working command.

Best Answer

With find:

find . -type f -exec sh -c 'SHELL COMMAND' {} \;

This invokes SHELL COMMAND on each found file in turn; the file name is "$0". Thus:

find . -type f -exec sh -c '
    mv "$0" "${0%/*}/$(printf "%s\n" "${0##*/}" | sha1sum | cut -d" " -f1)"
' {} \;

(Note the use of printf rather than echo, in case you have a file called -e or -n or a few other problematic cases that echo mangles.)

You can make this a little faster by invoking the shell in batches.

find . -type f -exec sh -c 'for x; do
      mv "$x" "${x%/*}/$(printf "%s\n" "${x##*/}" | sha1sum | cut -d" " -f1)";
    done' _ {} +

In zsh, there's an easy way to match all the files in the current directory and its subdirectories recursively. The . glob qualifier restricts the matches to regular files, and D includes dot files.

for x in **/*(.D); do mv …; done

In bash ≥4, you can run shopt -s globstar and use **/* to match all files in the current directory and its subdirectories recursively. You'll need to filter regular files in the loop.

shopt -s globstar; GLOBIGNORE=".:.."
for x in **/*; do if [[ -f $x ]]; then mv …; done
Related Question