'Overwrote' files, space still occupied, are they lost?

Solution 1:

I think this is the problem: You should have created directories A, B, C ... Z. If you did, the mv command should have moved the files to those directories.

But if not, the mv command moves the files to files with those names, A, B, C ... and I think this is what you did.

To make the shellscript safer, you should make it create the directories (if they are not already there) before you start the moving.

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

for dir in "${dirs[@]}"
do
 mkdir -p $dir
done

If you want things to get even safer, you can also use mv with the -i option

   -i, --interactive
          prompt before overwrite

Solution 2:

@Sudodus already explained what went wrong, but here's a simpler version of your script for next time:

for letter in {a..z}; do 
    dir=${letter^}
    mkdir -p -- "$dir" 
    mv -- "$letter"* "${letter^^}"* "$dir"/
done

Explanation

  • for letter in {a..z}; do: {a..z} expands to all lower case letters between a and z:

    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    

    So this will iterate over all lower case letters, saving each as $letter.

  • dir=${letter^} : the syntax ${var^^} returns the contents of the variable $var with the first character in upper case (since this only has one character, that's all we need). So, if $letter is a, then ${letter^^} is A, and therefore $dir will be the upper case version of the current $letter.

  • mkdir -p -- "$dir" : create the directory. If it already exists, do nothing (-p). The -- signifies the end of the options, and is useful to protect against names starting with -.
  • mv -- "$letter"* "${letter^}"* "$dir" : move every file (or directory) to the relevant target.

The problem with this, is that it will also move any directories you may have. It won't move the target directories, because either they don't exist yet, or you will try to move them into themselves, but any existing dirs that aren't the target dir will be moved.

If that's a problem, you will have to do something like this:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done

Solution 3:

Instead of checking every file against a dictionary array which creates alot of iteration you can match files against patterns.

Very basic sort:

#!/bin/bash

videos=./videos
sorted=./sorted

# sort types link,move.
sort_type=link

find "$videos" -maxdepth 1 -type f \
   \( -name '*.avi' -o -name '*.mkv' -o -name '*.mp4' \) -print0 |

while IFS= read -r -d ''; do

    b=$(basename "$REPLY")
    c=${b::1}

    case $c in
        [a-zA-Z]) label=${c^} ;; [0-9]) label="0-9" ;; *) label="_" ;;
    esac

    [[ ! -d "$sorted/$label" ]] && mkdir -p "$sorted/$label"

    if [[ -L $sorted/$label/$b ]] || [[ -e $sorted/$label/$b ]]; then
        echo "File/link: '$b' exists, skipping."
        continue
    fi

    case $sort_type in
        link)
            ln -rfst "$sorted/$label" -- "$REPLY"
            ;;
        move)
               mv -t "$sorted/$label" -- "$REPLY"
            ;;
    esac
done

Solution 4:

Safeguard in your .bashrc:

alias mv="mv -n --backup=numbered"