Terminal commands to move files/folders from Downloads to their appropriate folder?

I'm learning python and I'm trying to write something that's actually useful to me instead of just going through a bunch of exercises in a book. The idea is instead of using python to find and move various files/folders that I would just use python to issue commands in the terminal to get the job done.

So what I'm trying to do is write a script that will look in the downloads folder (and sub directories) and check for certain file types and move the files to the appropriate folder. (.mp3 -> music, .doc -> documents, etc). I've written a fairly lengthy script that does just this but the problem is that it moves the files when instead I want it to move the folders containing the files.

For example if I download an album I don't want to move all the individual mp3's to the music folder I want to move the folder containing all the songs. Similarly if I download a TV show I don't want it to move all the episodes to videos, but rather move the folder containing the episodes to the right folder.

I can take care of the python stuff myself so what I'm looking for is a terminal command that will look in the downloads folder and any sub directories, check for a file type (e.g .mp3) and then move the folder that contains the files rather than the files themselves. I can't for the life of me figure out how to do this so just thought I'd try asking here for help. Much appreciated!

Probably a combination of find and if and cp -r

You have at least two options: one that is purely python-focused, and one that is more shell-focused.

For the more shell-focused option, you can test if a directory, or any of its subdirectories contain any mp3 files with this command:

[ $(find PATH_TO_DIRECTORY -name "*.mp3" | wc -l) -ne 0 ] && mv PATH_TO_DIRECTORY DESTINATION_DIRECTORY

This will move PATH_TO_DIRECTORY into DESTINATION_DIRECTORY if PATH_TO_DIRECTORY or any of its subdirectories contain one or more mp3 files.

Be careful when using the mv command to move files across different physical volumes, because if it fails for some reason, it may lead to data loss. It is generally safer to use cp -r, followed by a sha1sum check to see if the files copied correctly, and then deleting the files at the source. But if you're doing everything on the same partition, mv is generally safe.


For pure python-focused option, you can check if a directory contains mp3 files in python with something like:

import os

# ...

for filename in os.listdir(directory_path):
    if filename.endswith('.mp3'):
        # it exists!
        break

You can do it recursively in a function which returns true if it finds an mp3 file or if it finds a subdirectory, it returns the result of calling itself on that subdirectory.

You can also move directories in python using the shutil.move() function, so you don't really need any shell commands.

This may be a more readable option because it keeps the logic of the program in the same language. It may also perform faster. And it's a nice challenge to do if you're learning python. So I recommend this option.

But the shell option should work, too, it's just a bit dirty.

3 Likes

awesome this is exactly what I've been looking for! and yeah it's all on the same partition so it should be fine. thanks so much i'll start testing it now to see how it goes.

Edit: So i messed around with the shell command and made a text file in /home/downloads/test and changed the code to move it to documents, and what happened was it moved the downloads folder to the documents folder. here's the command i tried

[ $(find /home/kevin/Downloads -name "*.txt" | wc -l) -ne 0 ] && mv /home/kevin/Downloads Documents

Before that I tried

[ $(find /home/kevin/Downloads -name "*.txt" | wc -l) -ne 0 ] && mv /home/kevin/Documents Documents

but it returned

mv: cannot move '/home/kevin/Documents' to a subdirectory of itself, 'Documents/Documents'

can you clarify what you meant? I know the code I should be entering to make this work i.e

[ $(find /home/kevin/Downloads -name "*.txt" | wc -l) -ne 0 ] && mv /home/kevin/Downloads/test Documents

but the problem is this only works if I know the name of the sub directory and I would have to keep changing the code to make this work. I'm not sure? thanks for your help!

1 Like

You should not run it on your entire Downloads directory, because it will check the entire Downloads directory. You should run it for each directory within Downloads separately.

Also, the destination directory has to be the full path. Just writing Documents assumes it is in the current working directory.

So the command you're looking for is:

[ $(find /home/kevin/Downloads/test -name "*.txt" | wc -l) -ne 0 ] && mv /home/kevin/Downloads/test /home/kevin/Documents/

So, if /home/kevin/Downloads/test contains a txt file in it (or in any of its subdirectories), the entire /home/kevin/Downloads/test will be moved to /home/kevin/Documents/

that's the problem I've been trying to figure out. If i have to amend the script for each folder in the downloads directory then it kind of defeats the purpose of writing the script in the first place lol.

Well, you have to list all the directories contained in Downloads and run the command on each of them. You can do this in python, or in shell.

In python, use os.listdir() and os.path.isdir() functions.

In shell, a possible solution is a for loop:

for DIR in /home/kevin/Downloads/*/
do
    [ $(find "$DIR" -name "*.txt" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Documents/
done

(I haven't tested it, so it may contain errors, but it seems okay at first glance, it may fail for filenames with spaces, etc...)

1 Like

The problem with this approach is if, for example, you have a directory test in your Downloads directory, and this directory contains an mp3 file and a txt file. Should you move test to Documents or to Music? Because in the first case, the mp3 file will be moved to Documents, and in the second case the txt file will be moved to Music.

You need a pretty smart algorithm to figure that out, which is not easy and perhaps not even possible without human input.

1 Like

This looks good I'll test it now to see how it works, thanks!

yeah I get what you mean. For the most part though my downloads folder will contain directories with all the same file types contained in them. So if I download an album the folder will contain mp3's, if I download a tv series the folder will contain all avi's or mkv's or whatever. but I see what you mean this is going to be a lot harder than I first thought lol.

1 Like

It is tricky - I use Musicbrainz to move music to an organised music folder.

Generally I just download music to my SSD, then drop the folder in Audacious and drag tracks I will keep to Picard which will then remove them to the Musicbrainz folder and leave the trash behind. Automating the moving will move it to a Music folder, but not organise it and difficult to implement (detecting mp3, flac etc and then moving the folder won't work if you download an mp3 to Downloads either).

Similarly, using Plex, I can't move media files unless I know Plex will manage them (otherwise I need to scrape my Plex folders for files not showing up in Plex using the Webtools gadget) - so mostly new files go to a 'New' folder, and ones that show up as TV shows can be moved there, ones that show up as movies go to a Movies folder, and other ones go to generic 'video folder'.

I can't imagine doing it with a single script.

1 Like

it worked! I'll tinker around with it for a bit and see if I can get a working script but I imagine i'm going to run into a bunch of problems but I feel like this is a much better way to learn than just crunching through exercises from a book. Thanks a lot man! :slight_smile:

1 Like

A tracklist.txt file might mess up your plan. So first test for mp3 and, when the directories containing mp3s are already moved, then for test for txt. This is crude, though, and not very flexible, but fairly simple.

It goes without saying, but I'll say it anyway, if you're going to test scripts like that in your home directory, make sure you have a backup (well, make sure to have a backup regardless), as an error in the script could cause unwanted data loss (for example, due to overwriting files). Also, when testing, it's a good idea to add -i option to the mv command, just so you have to manually confirm when files are going to be overwritten.

1 Like

yeah there's definitely a lot of problems but if I can get it working in at least some cases, just as a way to keep my downloads folder clean then I'll be happy enough. any exceptions I can just move them the old fashioned way.

Keeping it simple - you're going to need to use temporary pre-sort directories I'd say. So a separate 'Music Inbox' folder before going to the library...

1 Like

hmm that's interesting I'll definitely keep this in mind!

What about two scripts? one that moves files from the downloads folder that's not contained in a sub directory, and one that moves files that are?

Well, for files that are directly in your Downloads directory, that's trivial:

mv /home/kevin/Downloads/*.mp3 /home/kevin/Music/
1 Like

Cool. So what I've done now is created two shell scripts. One is for documents and it looks in the downloads folder for .doc, .docx, .pdf, .epub, and .mobi files and just moves them to documents. The other is for music and videos and it uses that for loop that you posted earlier to check downloads and its sub directories for mp3's, flac, wavs and moves the folders containing those files to the music folder. it also looks for mp4s, mkvs, and avi's and moves the folders containing those files to the videos folder.

I'm going to fill my downloads folder with various files and folders containing various files and try running the script to see what happens. will report back.

1 Like

The first script works fine and the second script kind of works. it moved an album I downloaded to the music folder but I downloaded a tv show (file type mp4) and moved it to the music folder also. I checked the script to see if I had just made a mistake but it definitely says in the script to move mp4's to /home/kevin/Videos so Ive no idea why it moved it to music? here's the script as it is.

#MOVES MUSIC FROM DOWNLOADS TO MUSIC FOLDER

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.mp3" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Music/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.flac" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Music/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.wav" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Music/
done

#MOVES VIDEOS FROM DOWNLOADS TO VIDEOS FOLDER

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.mkv" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Videos/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.mp4" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Videos/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.avi" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Videos/
done

#MOVES IMAGES FROM DOWNLOADS TO PICTURES

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.jpg" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Pictures/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.png" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Pictures/
done

for DIR in /home/kevin/Downloads//
do
[ $(find "$DIR" "
.gif" | wc -l) -ne 0 ] && mv "$DIR" /home/kevin/Pictures/
done

Forum kindly sponsored by