Skip to content

Instantly share code, notes, and snippets.

@jamesstout
Last active April 9, 2021 18:48
Show Gist options
  • Save jamesstout/5930693 to your computer and use it in GitHub Desktop.
Save jamesstout/5930693 to your computer and use it in GitHub Desktop.
rename files to a random filename with 5 digits that contain random numbers and letters
#!/usr/bin/env bash
# IRC Issue from helperW
# Network: freenode
# Channel: #macosx
# I have a directory with 001.png 023.png 003.png ( around 800 files)
# I have to rename them to a random filename with : 5 digits that contain random numbers and letters
# ensure all vars are initialised and exit on error
set -o nounset
set -e
e_header() {
printf "\n$(tput setaf 7)%s$(tput sgr0)\n" "$@"
}
# debug logging
e_debug() {
printf "$(tput setaf 2)%s$(tput sgr0)\n" "$@"
}
# Success logging
e_success() {
printf "$(tput setaf 64)✓ %s$(tput sgr0)\n" "$@"
}
# Error logging
e_error() {
printf "$(tput setaf 1)x %s$(tput sgr0)\n" "$@"
}
# Warning logging
e_warning() {
printf "$(tput setaf 136)! %s$(tput sgr0)\n" "$@"
}
# Ask for confirmation before proceeding
seek_confirmation() {
printf "\n"
e_warning "$@"
read -p "Continue? (y/n) " -n 1
printf "\n"
}
# Test whether the result of an 'ask' is a confirmation
is_confirmed() {
if [[ "$REPLY" =~ ^[Yy]$ ]]; then
return 0
fi
return 1
}
file_exists() {
if [ -e "$1" ]; then
return 0
fi
return 1
}
# this is only used to prepare test files
prepSampleFiles(){
numFiles=100
# see if first param is really set then check if it is a number
if [ -n "$1" ]; then
if ! [[ "$1" =~ ^[0-9]+$ ]] ; then
e_error "Aborting sample file prep - parameter is not a number"
exit 33
else
numFiles=$1
fi
fi
# check if empty - in sub shell so we don't have to reset the shopts
files=($(shopt -s nullglob;shopt -s dotglob; echo *))
if [ ${#files[@]} -gt 0 ]; then
seek_confirmation "Directory not empty, do you want to empty the directory?"
if is_confirmed; then
$(shopt -s nullglob;shopt -s dotglob;rm *)
else
e_error "Aborting sample file prep - directory not empty"
exit 44
fi
fi
e_debug "Creating $numFiles random sample $ext files"
START=1
for i in $(eval echo "{$START..$numFiles}")
do
echo $RANDOM > "$i.$ext"
done
# create some dupes
e_debug "Creating 3 duplicate $ext files"
if file_exists "1.$ext"; then
cp 1.png xx1.$ext
cp 1.png xxx1.$ext
cp 1.png xxxx1.$ext
else
e_warning "Dupe files not created"
fi
e_success "prepSampleFiles complete"
exit 77
}
function usage {
me=$(basename ${BASH_SOURCE[0]})
e_header "Usage: $me [-p [NUM]] [-e EXT]"
echo
echo "Rename all $ext files in the current directory to have a unique 5 character alphanumeric name"
echo 'Usually just run with no options'
echo
echo 'Options:'
echo -e '\t-p - prepare 100 (default) sample files in the current directory'
echo -e '\t-p NUM - prepare NUM sample files in the current directory'
echo -e '\t-e EXT - file extension of the files to be renamed'
exit 55
}
printDupes(){
local_array=("${@}")
e_warning "Dupes:"
for index in ${!local_array[*]}
do
e_debug "${local_array[$index]}"
done
}
# disappointing bit of code repetition here
# could be refactored
perlRandom(){
declare -a moreDupes=()
local_dupes=("${@}")
e_header "Renaming ${#local_dupes[@]} dupe[s] with Perl"
for index in ${!local_dupes[*]}
do
newName=$(perl -e '@chars = ("A".."Z", "a".."z", 0 .. 9); $rand .= $chars[rand @chars] for 1..5; print $rand;')
e_debug "renaming ${local_dupes[$index]} to $newName.$ext"
# if the new file already exists, add to dupes array
# mv -nv means no overwriting and be verbose so we can match the output
if [[ $(mv -nv "${local_dupes[$index]}" $newName.$ext | tail -n1) =~ overwritten$ ]]; then
moreDupes=("${moreDupes[@]:+${moreDupes[@]}}" "${local_dupes[$index]}" )
e_warning "Dupe, not renaming"
fi
done
#echo "foo ${moreDupes[@]}" # this fails if there are no more dupes moreDupes[@]: unbound variable
count=${#moreDupes[@]} # but this is OK if moreDupes[@] is not set .. weird
if [ $count -gt 0 ]; then
e_debug "More dupes"
printDupes "${moreDupes[@]:+${moreDupes[@]}}"
perlRandom "${moreDupes[@]:+${moreDupes[@]}}"
else
e_success "All files renamed"
exit 0
fi
}
renameDupes(){
declare -a moreDupes=()
local_dupes=("${@}")
e_header "Renaming ${#local_dupes[@]} dupe[s] with $shasumCmd"
for index in ${!local_dupes[*]}
do
shahash=$($shasumCmd "${local_dupes[$index]}" | awk '{ print $1}');
newName=$(echo "$shahash" | cut -c -5)
e_debug "renaming ${local_dupes[$index]} to $newName.$ext"
# if the new file already exists, add to dupes array
# mv -nv means no overwriting and be verbose so we can match the output
if [[ $(mv -nv "${local_dupes[$index]}" $newName.$ext | tail -n1) =~ overwritten$ ]]; then
moreDupes=("${moreDupes[@]:+${moreDupes[@]}}" "${local_dupes[$index]}" )
e_warning "Dupe, not renaming"
fi
done
count=${#moreDupes[@]}
if [ $count -gt 0 ]; then
e_debug "More dupes"
printDupes "${moreDupes[@]:+${moreDupes[@]}}"
let "loopCount += 1"
if [ $loopCount -gt 4 ]; then
e_error "More dupes left. Trying Perl."
perlRandom "${moreDupes[@]:+${moreDupes[@]}}"
else
shasumCmd="shasum -a ${shaOps[$loopCount]}"
renameDupes "${moreDupes[@]:+${moreDupes[@]}}"
fi
else
e_success "All files renamed"
exit 0
fi
}
# default extension
ext="png"
# quick arg check
if [ $# -eq 1 -a "${1:-unset}" = '--' ] || [ $# -gt 4 ]; then
usage
fi
extOpt=""
prepOpt=""
extArg=0
prepArg=0
for opt in "$@"
do
case $opt in
-e ) extOpt="${2:-}"; extArg=1; shift ;;
-p ) prepOpt="prepSampleFiles ${2:-}"; prepArg=1; shift ;;
-h | --help) usage ;;
-*|--*) e_warning "Warning: invalid option $opt" && usage;;
* ) shift;;
esac
done
if [ $extArg -eq 1 ]; then
if [ -z "$extOpt" ]; then
e_error "-e must have an argument. e.g. png"
usage
fi
fi
# must set ext first
if [ -n "$extOpt" ]; then
#e_debug "extOpt is $extOpt"
ext="$extOpt"
fi
if [ -n "$prepOpt" ]; then
#e_debug "prepOpt is $prepOpt"
eval "$prepOpt"
fi
# if no bad options, then we get to here, i.e. no options
e_header "Starting rename..."
# array to capture dupes
declare -a dupes=()
# file listing should be case-insensitive
for file in $(shopt -s nocaseglob; ls *.$ext);
do
mdhash=$(md5 -q "$file");
newName=$(echo "$mdhash" | cut -c -5)
e_debug "renaming $file to $newName.$ext"
# if the new file already exists, add to dupes array
# mv -nv means no overwriting and be verbose so we can match the output
if [[ $(mv -nv $file $newName.$ext | tail -n1) =~ overwritten$ ]]; then
e_warning "Dupe, not renaming"
# the :+ operator is param expansion
# to get around the unbound variable error when array is empty
# see http://cloud.zoooot.com/Q3V4
dupes=("${dupes[@]:+${dupes[@]}}" "$file" )
fi
done
# counter to change the shasum args if needed
loopCount=0
# shasum args and command
# used for renaming dupes
declare -a shaOps=(1 224 256 384 512)
shasumCmd="shasum -a ${shaOps[$loopCount]}"
# do we have any dupes?
count=${#dupes[@]} # but this is OK if dupes[@] is not set - no unbound variable error .. weird
if [ $count -gt 0 ]; then
printDupes "${dupes[@]:+${dupes[@]}}"
renameDupes "${dupes[@]:+${dupes[@]}}"
else
e_success "All files renamed"
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment