Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Ok, this may sound like an odd request but it really has a good purpose! I have a Panasonic Plasma TV with an SD card slot, via which I can display a slideshow of JPEG photos. This is useful for preventing burn-in, if I step away from the TV for a few moments while cooking, or if I answer the phone, or whatever, it's handy to just start the slideshow to prevent a static image from showing up, and also let me see photos while I do something else.

The problem is the slideshow always starts from the first photo alphabetically, meaning I always see the same images and never get to the end. What I'd like to do is have a script on my Unix machine which I can run on the SD card (mounted at /media/sdcard) and have it randomize the names of all the files under that directory. Doesn't really matter how it randomizes them but I'd prefer purely numeric names (for no apparent reason)

Is there an easy way to accomplish this? Something like find /media/sdcard -exec mv {} rand() \;?

For bonus points, any way to prevent the script from overwriting two files in the event that there's a collision? These are copies of photos so if I lose one I still have the original, but still that would be inconvenient...

share|improve this question

3 Answers 3

up vote 4 down vote accepted
find . -type f |
shuf |  # shuffle the input lines, i.e. apply a random permutation
nl -n rz |  # add line numbers 000001, …
while read -r number name; do
  ext=${name##*/}  # try to retain the file name extension
  case $ext in
    *.*) ext=.${ext##*.};;
    *) ext=;;
  esac
  mv "$name" "../randomized/${name%/*}/$number$ext"
done

Replace mv by ln or ln -s and possibly a different target directory as you see fit. Note that since find may still be traversing the directory by the time mv runs, you shouldn't rename or link the file inside the same directory.

shuf is specific to GNU coreutils, the rest is POSIX. If you're not on Linux or Cygwin, see alternatives in awk or Perl.

share|improve this answer
    
Wow that works perfectly, thanks! (I forgot to mention I was on Ubuntu) –  Josh Jan 27 '11 at 21:15
    
Because this adds line numbers, and then just uses the numbers for the new name, there's no possibility of collisions right? (Or am I understanding the code wrong?) –  Josh Jan 27 '11 at 21:18
    
@Josh: Right, the file names will use successive integers. The numbers will be globally unique even if there are subdirectories. –  Gilles Jan 27 '11 at 21:20

There's nothing Unix specific here...

import random, os

input_path = "/home/badp/Youtube/" #absolute path for simplicity
output_path = "./playlist"
files = os.listdir(input_path) #assume all files in the same folder for simplicity
random.shuffle(files)

os.mkdir(output_path)
os.chdir(output_path)
i = 0 
for filename in files:
  os.link( os.path.join(input_path, filename),
           str(i) + filename[filename.rfind(os.path.extsep):]
         )         #↑ there must be something cleaner than this :)
  i += 1

This'll break if there are folders in input_path (which you can't hardlink), but you can easily rewrite the file "hunting" logic to something much more robust (recursive search etc.) with the help of os.path and os.walk.

share|improve this answer
    
Oh, and this should work on Windows + NTFS too! –  badp Jan 27 '11 at 20:58
    
I'll try this badp... I agree there's nothing unix-specific about your script since it's just python. I was posting here to see if there was some sequence of unix command which could do it but in reality python works too. Except the card is FAT32 formatted, will that support hardlinks? –  Josh Jan 27 '11 at 21:12
    
@Josh No, then you can just copy :) –  badp Jan 27 '11 at 21:52

I see an accepted answer and another good answer. Anyway here is a script that I have been using. It randomizes the names by changing them to a hash sequence computed by the full path. Because the full path is unique to each system it is expected that file names don't clash (this is not fool-proof and adding a check would be easy but I didn't bother). This script renames files recursively but leave directory names intact. Use it by passing the folder to apply recursive rename as a parameter.

#! /bin/bash                                                                                                                                             

do_rename() {
    (
    export prevdir="$(pwd)"
    cd "$1"
    for file in *
    do
        if [ -d "$file" ]
        then
            echo "$file is a directory, renaming recursively"
            do_rename "$file"
        elif [ -f "$file" ]
        then
            dirpath="$prevdir/$1"
            oldname="$dirpath/$file"
            name_hash=$(echo "$oldname" | md5sum)
            newname="$dirpath/${name_hash:0:32}.jpg"
            echo "mv $oldname $newname"
            mv $oldname $newname
        fi
    done
    cd "$prevdir"
    )
}

do_rename "$1"
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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