1

I'm trying to execute this bash script that loops over files (that have spaces in the name) in the current directory and creates a new folder with the first character of the file (if not already created) and moves that file to the folder. This is my code:

for i in `/bin/ls | xargs`
do
    dir=`echo "$i" | cut -c 1 -`
    mkdir -m777 -p "$dir"
mv "$i" "$dir"
done

The problem with this seems to be that it treats the each word in the file as a separate file, so although it creates the folder correctly , it can't move the file to that folder because the name of the file that it looks for is only the first word of the actual file. I looked at other answers to similar questions but this is the closest I've been able to get.

EDIT: I replaced " for i in /bin/ls | xargs " with " for i in * " as @steeldriver suggested, and although it fixed my original problem, I'm getting errors like these:

mv: cannot move '`' to a subdirectory of itself, '`/`'
mv: invalid option -- ' '
mv: missing destination file operand after '-'
mv: invalid option -- '.'
mv: invalid option -- ')'
mv: invalid option -- '+'
mv: cannot move ''$'\340' to a subdirectory of itself, ''$'\340''/'$'\340'
mv: cannot move ''$'\303' to a subdirectory of itself, ''$'\303''/'$'\303'
mv: cannot move ''$'\305' to a subdirectory of itself, ''$'\305''/'$'\305'
mv: invalid option -- '1'

I think some of these files may start with non-ascii characters (I can't view the contents because there are too many files). Is there a work around to handle these cases?

Logan
  • 121
  • 3

2 Answers2

2

To loop over files with spaces in their names, the shell is plenty, no need to call ls:

for    i in *                   # * replaces the complex (and unquoted) `/bin/ls | xargs`
do
       dir=${i%"${i#?}"}        # replaces the slow subshell `echo "$i" | cut -c 1 -`

       echo "$i"                # just to show that an * is enough (and accepts spaces).
done

And to process each file listed (which include directories) you should check that the filename is a file (not a directory) and also check if the directory doesn't exist before creating it.

for i in *
do
    if [ -f "$i" ]; then
        dir=${i%"${i#?}"}
        if [ ! -d "$dir" ]; then
            mkdir -m777 -p "$dir"
        fi
        mv "$i" "$dir"
        if [ "$?" -ne 0 ]; then
            echo "An error occurred moving file \"$i\" to dir \"$dir\""
        fi
    fi
done
0

With GNU Parallel it looks like this:

parallel 'mkdir -p {=s/(.).*/$1/=}; mv {} {=s/(.).*/$1/=}' ::: *

(Edit: Just noted you are asking for files - not dirs. / is removed).

Ole Tange
  • 35,514
  • When I try this, I get the error: mv: cannot move '/' to a subdirectory of itself, '/*' – Logan May 07 '19 at 22:13