10

I have a directory (directory A) with 10,000 files in it. I want to move some of them to directory B and the others to directory C. I made a text file that contains the names of all the files I want to move to directory B and another one with the names of all the files that I want to move to directory C. How can I write a bash for loop to move these files to the new directories.

Pseudocode:

for file in textfileB:
move file from directory A to directory B

for file in textfileC:
move file from directory A to directory C

Sorry if this is asked somewhere else, but I've spent hours trying to learn bash and I just don't get it. I wasn't able to find something similar enough in another thread that I could understand (maybe I just don't know the right search words).

I got something like this, but I couldn't get it working:

FILES=[dont' know what goes here? An array? A list?  

Can I just state the text file name and if so what format do the files have to be? name1.ext, name2.ext, or name1.ext name2.ext]

for f in $FILES; do mv $f /B/$f [not sure about the second argument for mv]; done

thx

BTW Mac OSX 10.6.8 (Snow Leopard) Apple Terminal v. 2.1.2 / 273.1 Bash 3.2

PatentDeathSquad
  • 543
  • 2
  • 7
  • 16

6 Answers6

23
cat file-list.txt | while read i; do
   # TODO: your "mv" command here.  "$i" will be a line from
   # the text file.
done
asveikau
  • 39,039
  • 2
  • 53
  • 68
  • 1
    Thanks. This worked. For others, here is the completed command, which I entered as a single line: cat list_of_files_with.txt | while read i; do mv $i /Volumes/hard_drive_name/new_destination_directory_name; done – PatentDeathSquad Jul 04 '11 at 03:45
  • 1
    @AquaT33nFan - I would put `"$i"` in quotes so that you can handle filenames with spaces and other characters that don't typically appear in a command line. – asveikau Jul 04 '11 at 03:57
  • 1
    Congratulations, you just received the [UUoCA](http://partmaps.org/era/unix/award.html) – Fredrik Pihl Jul 04 '11 at 08:29
  • @Fredrik - Is it really useless if it increases readability? I find something like `(while read i; do whatever; done) < file-list.txt` to be "backwards" to read. Also, the extra context switches and loading of the `cat` binary are going to be pretty cheap, as in you won't notice the difference on current hardware. – asveikau Jul 04 '11 at 18:48
  • @asveikau - "useless" is perhaps a too strong word here, but I would argue that the approach in your comment is superior the the `cat ... | ` approach. And since \*NIX is a very beautiful OS, you wouldn't want to wast processes, do you? ;-) – Fredrik Pihl Jul 04 '11 at 21:06
  • I liked the cat one better. It reads like python and I don't know bash well, which I said in my question. The other answer (which appears to redirect standard output) is a little harder. It did take a few minutes to do this though, so if I had more files I might need a way with fewer processes. Thx. – PatentDeathSquad Jul 05 '11 at 09:59
3

BASH FAQ entry #1: "How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?"

If the filename will remain the same then the second argument to mv can be just the directory.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

you can move 1000 or 1000 user directory without take much time where thousand of user directory exist.

cat deleteduser | while read i;  do mv -vv $i ../deleted_user; done; 
deleteuser= user name list
../deleted_user= destination dir
antyrat
  • 27,479
  • 9
  • 75
  • 76
1

directory of the script should be the your location of the files

TO_B=file1.txt
TO_C=file2.txt

for file in $TO_B
do
mv ${file} B/
done

for file in $TO_C
do
mv ${file} C/
done
Vijay
  • 65,327
  • 90
  • 227
  • 319
0

Using bash, having a huge filelist containing strings with leading and/or closing spaces I'd propose:

less 'file-list.txt' | while read -r; do mv "$REPLY" /Volumes/hard_drive_name/new_destination_directory_name; done

see:

anon
  • 1
0

You have to use BASH? What about Perl or Kornshell? The problem here is that Bash doesn't have hash keyed arrays (Kornshell and Perl do). That means there's no simple way to track what files go where. Imagine in Perl:

my %directoryB;   #Hash that contains files to move to Directory B
my %directoryC;   #Hash that contains files to move to Directory C

open (TEXT_FILE_B, "textFileB") or die qq(Can't open "textFileB");
while (my $line = <TEXT_FILE_B>) {
    chomp $line;
    $directoryB{$line} = 1;
}
close TEXT_FILE_B;

open (TEXT_FILE_C, "textFileC") or die qq(Can't open "textFileC");
while (my $line = <TEXT_FILE_C>) {
    chomp $line;
    $directoryC{$line} = 1;
}
close TEXT_FILE_C;

The above lines create two hashes. One for files that need to be moved to Directory B and one for files that need to be moved to Directory C. Now, all I have to do is look at my hash and decide:

foreach my $file (@directory) { #A little cheating here...
   if (exists $directoryB{$file}) {
       move ($file, $directoryB);
   } elsif (exists $directoryC{$file}) {
       move ($file, $directoryC)
}

My if statements can now look to see if a key has been defined in that hash. If it has, I know the file can be moved into that directory. I only have to read the two text files once. After that, my two hashes will store which files move to one directory and which to the other.


However, we don't have hashes, so we'll use grep to see if the file name is in the directory. I'll assume that you have one file name on each line.

ls | while read file
do
   if grep -q "^${file}$" textFileB
   then
       mv $file $directoryB
   elif grep -q "^${file}$" textFileC
   then
       mv $file $directoryC
   fi
done

The grep -q will search your two text files to see if the matching file is there. If it is, it'll move the file to that directory. It's not very efficient since it has to search the entire text file each and every time. However, it's pretty efficient, and you're only talking about 10,000 files, so the whole operation should only take a few minutes.

David W.
  • 105,218
  • 39
  • 216
  • 337