Copy all images in a directory to sub-directories in batches of 30

All of my images are in a single folder which are in excess of 1.6K. I want to make a batch of 30 images from the folder, and copy that batch over to its respective separate folder. The names of these folders can be anything, like folder1, folder2, fodler3, and so on.

I have no idea how to approach the problem. I thought about writing a Python script to copy the files one by one and, as soon as the count reaches 30, change the folder. But I thought that maybe you guys have a better idea on how to do it with Bash.


Solution 1:

Use GNU parallel:

#!/bin/bash
mkdir_mv(){ target=$1; shift; mkdir "$target"; mv -t "$target" "$@"; }
export -f mkdir_mv
parallel -n 30 -j1 mkdir_mv "folder{#}" {} ::: *

As a normal mv won't create directories, we define a function first, that does that. We need to export the function to make it work with parallel

You will run the parallel command from the folder with your pictures.

Explanation:

  • -n 30 use 30 arguments for each parallel run
  • -j 1 run only 1 process in parallel (= do not run in parallel)
  • {#} the current run number
  • {} 30 arguments for each run.
  • ::: argument separator.
  • * The input files.

parallel can be installed with:

sudo apt install parallel

Solution 2:

You can use this Bash script:

#!/bin/bash
from="/path/to/parent"       # <--- replace this
to="/path/to/target"         # <--- replace this
i=1

until [ "$(ls -A "$from")" = "" ]; do
    mkdir "$to/folder$i"
    files=("$from"/*)
    for f in "${files[@]:0:30}"; do
        mv "$f" "$to/folder$i"
    done
    i=$((i + 1))
done

In the above script replace the path of the from variable with the absolute path to the directory containing your images and the path of the to variable with the absolute path to the directory you want to move your images in. The batches of 30 images will be moved in directories named as folder1, folder2, folder3, etc.

Save the script as batch_move.sh (or any name you wish) and make it executable by running:

chmod u+x /path/to/batch_move.sh

Then you can run the script as:

/path/to/batch_move.sh

Note: Just to be on the safe side, copy a portion of your images in another directory and test the script on that first. If it works as intended, then run it for all your images.

Solution 3:

You could use a for-loop with increment by 30.

#!/bin/bash

source=/opt/files/old
target=/opt/files/new

cd "$source" # && touch {000..300}

eval a=($( \
    ls --quoting-style=shell-escape) \
)
for ((b=0; b<${#a[@]}; b+=30)); do
    mkdir -p "$target/folder$((++c))" && mv -t "$_" "${a[@]:$b:30}"
done