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:
- Erase the USB drive (it would be used only for music)
- Random selection of albums (data amount cap? fixed number?)
- 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
- Copy the script into an empty file, save it as
create_mediausb.py
- 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.
-
Run the script with the command:
python3 /path/to/create_mediausb.py
-
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:
-
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
-
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)
-
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])
-
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