Your script is picking up directories and non-directory files and moving these into subdirectories based on their names, even if they are the single-letter directories that we are sorting things into. That's how the new directories appear. Your code also needlessly uses sed
and tr
etc., to do something that the bash
shell can do quicker and safer. Additionally, ls
should never be used in scripts for iterating over filenames (see, e.g. Why *not* parse `ls` (and what to do instead)? for why).
Another issue is that if you have many thousands of files with names starting with the same character, then the mv "$first"* tmp/
command (or the second mv
) may fail with an "argument list too long" error (see, e.g. Argument list too long for ls ; the issue applies to mv
and all other external commands, not just ls
).
An example script that works around these issues and that only uses functionality in bash
apart from mkdir
and mv
:
#!/bin/bash
shopt -s nullglob # makes the loop not loop at all if there are no files
for name in *; do
if [[ -d $name ]] && [[ $name == ? ]]; then
# skip directories that have single character names
continue
fi
# get first character in filename
first=${name:0:1}
# compute destination directory name from this first
# character by uppercasing it
destdir=${first^}
# make that directory if it does not already exist
mkdir -p -- "$destdir"
# move the file into place (name collisions not checked for)
mv -- "$name" "$destdir"
done
The --
options in the calls to mkdir
and mv
protect against filenames that may start with a dash (see, e.g. What does "--" (double-dash) mean?).
The main difference with this script is that I'm looping over the names in the directory rather than over the first characters, as you do.
#!/bin/bash
as the first line. Won't fix the problem but makes it clear you've written abash
script and not ash
one. – Chris Davies Mar 02 '20 at 09:02