A script for copying random folders

I'd like to realize a little project I've always had in mind. I have a vast music library on my desktop and I'm looking for a way/script to select a random amount of folders (say, 25 albums) and, if doable, copy them onto a USB drive for my car. My full vision would be a script able to do these steps:

  1. Erase the USB drive (it would be used only for music)
  2. Random selection of albums (data amount cap? fixed number?)
  3. Copy the selection to the USB drive

Is this doable via a simple script? I remember some music organizers had this option, but I was looking for something simpler for a headless server.


Solution 1:

you could use find and shuf:

#!/bin/bash

SOURCE="path/to/source"
DESTINATION="path/to/destination"
COUNT=25

rm -r "${DESTINATION}/"*
find "$SOURCE" -mindepth 2 -maxdepth 2 -type d|shuf -n $COUNT|xargs -d'\n' -I{} cp -r "{}" "$DESTINATION"

Solution 2:

The script below should do the job:

#!/usr/bin/env python3
import random
import os
import subprocess
import shutil

# set the desired number of folders to pick below
n_selection = 5
# set the name of the flash drive below
flashdr = "Lexar"
# set the source directory (with media folders) below
sourcedr = "/path/to/mediafiles"
# ---

try:
    targetdr = [l.split("part ")[-1] for l in subprocess.check_output("lsblk")\
                .decode("utf-8").splitlines()if l.endswith(flashdr)][0]
except IndexError:
    pass
else:
    # empty the flash drive
    for item in os.listdir(targetdr):
        obj = os.path.join(targetdr, item)
        try:
            shutil.rmtree(obj)
        except NotADirectoryError:
            os.remove(obj)
    # list the source dirs
    srclist = []
    for dr in  os.listdir(sourcedr):
        fullpath = os.path.join(sourcedr, dr)
        if os.path.isdir(fullpath):
            srclist.append([dr, fullpath])
    # copy the files
    for picked in random.sample(srclist, n_selection):
        shutil.copytree(picked[1], os.path.join(targetdr, picked[0]))
        srclist.remove(picked)

It copies directories from the first sub-level of the source-directory into the targeted flash drive.

This is what seems to make the most sense, since recursively copying folders, at random, causes a big difference in folder- size and number sub-levels. I added it nevertheless as a second option at the bottom of this answer.

How to use

  1. Copy the script into an empty file, save it as create_mediausb.py
  2. In the head section, set the number of files to be selected, the name of the flash drive (the script will find its path) and the source diectory with folders.
  3. Run the script with the command:

    python3 /path/to/create_mediausb.py 
    
  4. If all works fine, add it to a shortcut key: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

    python3 /path/to/create_mediausb.py 
    

Second option: recursive selection

#!/usr/bin/env python3
import random
import os
import subprocess
import shutil

n_selection = 5
flashdr = "Lexar"
sourcedr = "/home/jacob/Bureaublad/GW_site_nafestival_2015/pix/general"

try:
    targetdr = [l.split("part ")[-1] for l in subprocess.check_output("lsblk")\
                .decode("utf-8").splitlines()if l.endswith(flashdr)][0]
except IndexError:
    pass
else:
    # empty the flash drive
    for item in os.listdir(targetdr):
        obj = os.path.join(targetdr, item)
        try:
            shutil.rmtree(obj)
        except NotADirectoryError:
            os.remove(obj)
    # list the source dirs
    srclist = []
    for root, dirs, files in os.walk(sourcedr):
        for dr in dirs:
            srclist.append([dr, os.path.join(root, dr)])
    # copy the files
    for picked in random.sample(srclist, n_selection):
        shutil.copytree(picked[1], os.path.join(targetdr, picked[0]))
        srclist.remove(picked)

The script:

  1. Looks up the path to the flash drive:

    try:
        targetdr = [l.split("part ")[-1] for l in subprocess.check_output("lsblk")\
                    .decode("utf-8").splitlines()if l.endswith(flashdr)][0]
    except IndexError:
        pass
    

    If the flash drive is not found, it ends here

  2. If the drive was found, it is emptied

    # empty the flash drive
    for item in os.listdir(targetdr):
        obj = (targetdr+"/"+item)
        try:
            shutil.rmtree(obj)
        except NotADirectoryError:
            os.remove(obj)
    
  3. Then, since we need the whole directory list to make an appropriate selection at random, we create a list before making the selection:

    # list the source dirs
    srclist = []
    for dr in  os.listdir(sourcedr):
        fullpath = sourcedr+"/"+dr
        if os.path.isdir(fullpath):
            srclist.append([dr, fullpath])
    
  4. The most interesting part is making the selection. We pick a random directory, remove it from the list to prevent double picks, then pick another one at random, remove it, and so on, until we reach the number of desired picks:

    # copy the files
    for picked in random.sample(srclist, n_selection):
        shutil.copytree(picked[1], os.path.join(targetdr, picked[0]))
        srclist.remove(picked)
    

Solution 3:

I made a script with Python:

The script, at GitHub Gist. (Download)

Usage:

python3 RandomCopier.py [source folder] [destination folder] [number to copy]

The copy method:

NOTE: It will not copy any files directly in the source folder, only those in sub-folders of it.

Say, the source folder, src is:

src
|- a
|  |- file_a
|  |- file_a_2
|
|- b
|  |- file_b
|
|- c
|  |- file_c
|
|- file_src

then, the destination folder dest would be, with 2 folders randomly copied, like:

dest
|- a
|  |- file_a
|  |- file_a_2
|
|- c
|  |- file_c