'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 betweena
andz
:$ 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
isa
, then${letter^^}
isA
, 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"