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
You don't need to use awk
at all. Use the built-in tests that ksh
provides, something like this:
#!/bin/ksh
for NAME in *
do
FOUND=no
if [[ -d $NAME && $NAME != '.' && $NAME != '..' ]]
then
for SUBNAME in $NAME/*
do
if [[ -d $SUBNAME ]]
then
FOUND=yes
break
fi
done
if [[ $FOUND == no ]]
then
echo Found only files in $NAME
fi
fi
done
That little script looks in all the directories in the current directory, and tells you if they only contain files, no sub-directories.
Best Answer
Here is a completely different approach based on GNU
find
anduniq
. This is much faster and much CPU-friendly than answers based on executing a shell command that counts files for each directory found.The
find
command prints the directory of all files in the hierarchy anduniq
only displays the directories that appear at least twice.