Tell me more ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Say I'm in a directory and I want to add a hash to each file in that directory and all sub-directories and add the hash into the filename.

ie:
filename.extension
to
filename.hashcode.extension

search -> perform hash -> use returned hash to rename file

Is there an idiomatic way of recursively doing operations like these in BASH?

I reposted this on StackOverflow LINK
The chosen answer was:

 #!/bin/bash
if (("${#}" != 1)) || ! [[ -d "${1}" ]]; then
    echo "Usage: $0 /path/to/directory"
    exit 1
fi

is_hash() {
    md5="${1##*.}" # strip prefix
    [[ "${md5}" == *[^[:xdigit:]]* || "${#md5}" -lt 32 ]] \
    && echo "${1}" || echo "${1%.*}"
}

while IFS= read -r -d $'\0' file; do
    read hash junk < <(md5sum "${file}")
    basename="${file##*/}"
    dirname="${file%/*}"
    pre_ext="${basename%.*}"
    ext="${basename:${#pre_ext}}"

    # File already hashed?
    pre_ext=$(is_hash "${pre_ext}")
    ext=$(is_hash "${ext}")

    mv "${file}" "${dirname}/${pre_ext}.${hash}${ext}" 2> /dev/null

done < <(find "${1}" -path "*/.*" -prune -o \( -type f -print0 \))
share|improve this question

closed as off topic by Diago Dec 4 '09 at 4:39

Questions on Super User are expected to relate to computer software or computer hardware within the scope defined in the FAQ. Consider editing the question or leaving comments for improvement if you believe the question can be reworded to fit within the scope. Read more about closed questions here.

2 Answers

up vote 2 down vote accepted

This would likely require you to write a script. Things to look at are as follows:

The pseudocode for such a script would probably look something like the following:

  1. Use find to make a list with the absolute path of all the interesting files.
  2. With the help of a for loop in bash, call md5sum on each file named in the list.
  3. Use cut to get just the hash from the output of md5sum and remove the junk.
  4. Use sed to parse the filename for the last dot where the hash is to be inserted.
  5. Concatenate the three parts of the filename together as needed from the variables.
  6. Call mv to rename the file with its fancy new hash-bearing filename.

The folks over at Stack Overflow would be able to help you with the details of your script.

Note: My answer presumes that your preferred hashing algorithm is MD5. This is my mistake. Perhaps you would prefer something like SHA-1 or SHA-256 instead. In any case, all you would need is a program capable of hashing the file as needed.

share|improve this answer
re: Note: you mean like sha1sum or sha256sum? they're the analogues of md5sum for those algorithms. – quack quixote Dec 3 '09 at 23:08

This might point you in the right direction:

#!/bin/bash
find . -name '*.*' -type f -print | while read file; do
 hash=$(md5sum $file | awk '{print $1}')
 echo $hash
 mv $file ${file%.*}.$hash.${file/*.}
done

Here generate_hash should be substituted with whatever tool you use to generate the hash value. If you were using md5sum, the command might actually look like $(md5sum $file | awk '{print $1}').

The 'mv' command makes use of bash-specific variable expansion syntax. Learning this will make your life much easier!

To stave off any knee-jerk responses: this is intentionally looking for '.' not because I'm some sort of DOS-brained goofball but because the script will break if the 'mv' command gets a filename without a dot in it.

share|improve this answer
nice script; i didn't know bash could do substrings like that. it doesn't handle files with spaces in them, tho. – quack quixote Dec 3 '09 at 23:21
fix for spaces-in-filenames: do this before the find/while: oldIFS=$IFS; IFS='\n' ... do this after the find/while: IFS=$oldIFS – quack quixote Dec 3 '09 at 23:29
... Or just put all instances of $file in double-quotes: mv "$file" "${file%.*}.$hash.${file/*.}" – greyfade Dec 4 '09 at 7:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.