Bash Script to Prepend a Single Random Character to All Files In a Folder

Viewed 142

I have an audio sample library with thousands of files. I would like to shuffle/randomize the order of these files. Can someone provide me with a bash script/line that would prepend a single random character to all files in a folder (including files in sub-folders). I do not want to prepend a random character to any of the folder names though.


Example:
Kickdrum73.wav
Kickdrum SUB.wav
Kick808.mp3

Renamed to:
f_Kickdrum73.wav
!_Kickdrum SUB.wav
4_Kick808.mp3


If possible, I would like to be able to run this script more than once, but on subsequent runs, it just changes the randomly prepended character instead of prepending a new one.

Some of my attempts:

find ~/Desktop/test -type f -print0 | xargs -0 -n1 bash -c 'mv "$0" "a${0}"'    
find ~/Desktop/test/ -type f -exec mv -v {} $(cat a {}) \;   
find ~/Desktop/test/ -type f -exec echo -e "Z\n$(cat !)" > !Hat 15.wav    
for file in *; do    
    mv -v "$file" $RANDOM_"$file"    
done

Note: I am running on macOS.


Latest attempt using code from mr. fixit:

find . -type f -maxdepth 999 -not -name ".*" |
cut -c 3- - |
while read F; do
    randomCharacter="${F:2:1}"
    if [ $randomCharacter == '_' ]; then
        new="${F:1}"
    else
        new="_$F"
    fi
    fileName="`basename $new`"
    newFilename="`jot -r -c $fileName 1 A Z`"
    filePath="`dirname $new`"
    newFilePath="$filePath$newFilename"
    mv -v "$F" "$newFilePath"
done
3 Answers

Here's my first answer, enhanced to do sub-directories.

Put the following in file randomize

if [[ $# != 1 || ! -d "$1" ]]; then
  echo "usage: $0 <path>"
else
  find $1 -type f -not -name ".*" |
    while read F; do
      FDIR=`dirname "$F"`
      FNAME=`basename "$F"`
      char2="${FNAME:1:1}"
      if [ $char2 == '_' ]; then
        new="${FNAME:1}"
      else
        new="_$FNAME"
      fi
      new=`jot -r -w "%c$new" 1 A Z`
      echo mv "$F" "${FDIR}/${new}"
    done
fi

Set the permissions with chmod a+x randomize.

Then call it with randomize your/path.

It'll echo the commands required to rename everything, so you can examine them to ensure they'll work for you. If they look right, you can remove the echo from the 3rd to last line and rerun the script.

cd ~/Desktop/test, then

find . -type f -maxdepth 1 -not -name ".*" |
    cut -c 3- - |
    while read F; do
        char2="${F:2:1}"
        if [ $char2 == '_' ]; then
            new="${F:1}"
        else
            new="_$F"
        fi
        new=`jot -r -w "%c$new" 1 A Z`
        mv "$F" "$new"
    done

find . -type f -maxdepth 1 -not -name ".*" will get all the files in the current directory, but not the hidden files (names starting with '.')

cut -c 3- - will strip the first 2 chars from the name. find outputs paths, and the ./ gets in the way of processing prefixes.

while read VAR; do <stuff>; done is a way to deal with one line at a time

char2="${VAR:2:1} sets a variable char2 to the 2nd character of the variable VAR.

if - then - else sets new to the filename, either preceded by _ or with the previous random character stripped off.

jot -r -w "%c$new" 1 A Z tacks random 1 character from A-Z onto the beginning of new

mv old new renames the file

You can also do it all in bash and there are several ways to approach it. The first is simply creating an array of letters containing whatever letters you want to use as a prefix and then generating a random number to use to choose the element of the array, e.g.

#!/bin/bash

letters=({0..9} {A..Z} {a..z})                      ## array with [0-9] [A-Z] [a-z]

for i in *; do
    num=$(($RANDOM % 63))                           ## generate number
    ## remove echo to actually move file
    echo "mv \"$i\" \"${letters[num]}_$i\""         ## move file
done

Example Use/Output

Current the script outputs the changes it would make, you must remove the echo "..." surrounding the mv command and fix the escaped quotes to actually have it apply changes:

$ bash ../randprefix.sh
mv "Kick808.mp3" "4_Kick808.mp3"
mv "Kickdrum SUB.wav" "h_Kickdrum SUB.wav"
mv "Kickdrum73.wav" "l_Kickdrum73.wav"

You can also do it by generating a random number representing the ASCII character between 48 (character '0') through 126 (character '~'), excluding 'backtick'), and then converting the random number to an ASCII character and prefix the filename with it, e.g.

#!/bin/bash

for i in *; do
    num=$((($RANDOM % 78) + 48))                         ## generate number for '0' - '~'
    letter=$(printf "\\$(printf '%03o' "$num")")         ## letter from number
    while [ "$letter" = '`' ]; do                        ## exclude '`'
        num=$((($RANDOM % 78) + 48))                     ## generate number
        letter=$(printf "\\$(printf '%03o' "$num")")
    done
    ## remove echo to actually move file
    echo "mv \"$i\" \"${letter}_$i\""                    ## move file
done

(similar output, all punctuation other than backtick is possible)

In each case you will want to place the script in your path or call it from within the directory you want to move the file in (you split split dirname and basename and join them back together to make the script callable passing the directory to search as an argument -- that is left to you)

Related