0

Im trying to sort thousands of files and put them in their own folder based on file extension. For example, JPG files to go into a JPG folder.

How can I create a for loop to address this?

What I have attempted to far:

# This listed out all the file extensions and the count for each extension:
find . -type f | rev | cut -d. -f1 | rev  | tr '[:upper:]' '[:lower:]' | sort | uniq --count | sort -rn
#This was able to find all jpegs in the media folder
find /media -iname '*.jpg' 
# This worked, however the MV command does not create the folder
find /media -iname '*.jpg' -exec mv '{}' /media/genesis/Passport/consolidated/jpg/ \; # 

Im guessing that the for loop would be something like but I cant seem to figure it out:

for dir in 'find /media -iname "*.jpg"'; do
    mkdir $dir;
    mv $dir/*;
done
Howie B
  • 21
  • 3
  • 1
    How many levels of directory are in `/media`? It's possible using `find`, but if you are finding recursively, and moving to a single directory, you have to decide how to handle duplicate file names. Especially if there's lots of `DSC_0001.JPG`. There's also `exiftool`, which can create file and directory naming schemes from the images' meta data. – dan Nov 14 '21 at 16:15
  • in your loop check *'echo ${dir##\*/}; echo ${dir%/\*}'* – alecxs Nov 14 '21 at 16:18
  • @dan There is only one level of folders, but there are about 100 folders. Each one has about 500-600 files. (I had to recover a media hard drive that has a partition erased, and the file structure was not maintained) – Howie B Nov 14 '21 at 16:24
  • 1
    If you use `rename`, also known as `Perl rename` or `prename`, you will have several benefits 1) you don't need a loop, 2) you don't need to create output directories, as it makes them for you, 3) you can do a *"dry run"*, 4) you can use the power of Perl regexes to extract, lower-case and fiddle with filenames and 5) it won't clobber files if you move `1.jpg` from two different directories into a common destination. – Mark Setchell Nov 14 '21 at 17:29

1 Answers1

0

To find all the files of a particular extension and move then to a destination we can use find command as follows:

find . -name "*.jpg" -exec mv {} $destination/ \;

This being the critical logic, you can just create a for loop on top of this to iterate through all the known file extensions in your media folder

## List all the known extensions in your media folder.
declare -a arr=("jpg" "png" "svg")

## Refer question 1842254 to get all the extentsions in your folder.
## find . -type f | perl -ne 'print $1 if m/\.([^.\/]+)$/' | sort -u


## now loop through the above array
for i in "${arr[@]}"
do
   echo "$i"
   ## Create a directory based on file extension
   destination="$i"
   mkdir $destination
   find /media -iname "*.$i" -exec mv {} $destination/ \;
done

Note that you can change your destination to be whatever you want. I just left it in the same directory.

Here is an executable shell script for your reference: https://onlinegdb.com/NEQSpojhM

viv3k
  • 621
  • 4
  • 10
  • this run entire disk three times. it won't find JPG – alecxs Nov 14 '21 at 17:23
  • You can use `-iname` flag with `find` command to make it case-insensitive. And as for running the disk three-times, I took the liberty of being non-optimal since this is only going to be a one time job :) – viv3k Nov 14 '21 at 17:47
  • yes that's how OP did. you should not answer my comments but edit your answer to avoid getting downvoted. consider [optimizing](https://unix.stackexchange.com/a/55973) your non-optimal code ;) – alecxs Nov 14 '21 at 17:51