Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux: Move 1 million files into prefix-based created Folders

I have a directory called "images" filled with about one million images. Yep.

I want to write a shell command to rename all of those images into the following format:

original: filename.jpg
new: /f/i/l/filename.jpg

Any suggestions?

Thanks,
Dan

like image 208
Dan Avatar asked Aug 07 '09 02:08

Dan


People also ask

How do I move 100 files from one directory to another in Linux?

The mv (move) command is used to move one or more files or directories from one directory to another directory using terminal in the Linux/Unix operating system. After using the mv command file is copied from source to destination and source file is removed. The mv command is also used to rename the file.

How do I move the first 1000 files in Linux?

For this to work as-is dir2 must exist and you have to execute it from the parent directory of dir1 and dir2 . This will move 1000 files from dir1 to dir2. Note that ls -Q does not produce an output compatible with xargs 's expected input format.

How do I move a large number of files in Linux?

find command We can use the find command along with other commands like the mv command or the xargs command to move a large number of files. The xargs command is used to build and execute command lines from standard input. In the above command, replace the destination to where you want to copy the files.


2 Answers

for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done;

The ${i:0:1}/${i:1:1}/${i:2:1} part could probably be a variable, or shorter or different, but the command above gets the job done. You'll probably face performance issues but if you really want to use it, narrow the *.* to fewer options (a*.*, b*.* or what fits you)

edit: added a $ before i for mv, as noted by Dan

like image 59
inerte Avatar answered Sep 22 '22 11:09

inerte


You can generate the new file name using, e.g., sed:

$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'
t/e/s/test.jpg

So, you can do something like this (assuming all the directories are already created):

for f in *; do
   mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')"
done

or, if you can't use the bash $( syntax:

for f in *; do
   mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`"
done

However, considering the number of files, you may just want to use perl as that's a lot of sed and mv processes to spawn:

#!/usr/bin/perl -w
use strict;

# warning: untested
opendir DIR, "." or die "opendir: $!";
my @files = readdir(DIR); # can't change dir while reading: read in advance
closedir DIR;
foreach my $f (@files) {
    (my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/;
    -e $new_name and die "$new_name already exists";
    rename($f, $new_name);
}

That perl is surely limited to same-filesystem only, though you can use File::Copy::move to get around that.

like image 39
derobert Avatar answered Sep 22 '22 11:09

derobert