0

I have a folder filled with ~300 files. They are named in this form username@mail.com.pdf. I need about 40 of them, and I have a list of usernames (saved in a file called names.txt). Each username is one line in the file. I need about 40 of these files, and would like to copy over the files I need into a new folder that has only the ones I need.

Where the file names.txt has as its first line the username only: (eg, eternalmothra), the PDF file I want to copy over is named eternalmothra@mail.com.pdf.

while read p; do
  ls | grep $p > file_names.txt
  done <names.txt

This seems like it should read from the list, and for each line turns username into username@mail.com.pdf. Unfortunately, it seems like only the last one is saved to file_names.txt.

The second part of this is to copy all the files over:

while read p; do
    mv $p foldername
    done <file_names.txt

(I haven't tried that second part yet because the first part isn't working).

I'm doing all this with Cygwin, by the way.

1) What is wrong with the first script that it won't copy everything over? 2) If I get that to work, will the second script correctly copy them over? (Actually, I think it's preferable if they just get copied, not moved over).

Edit: I would like to add that I figured out how to read lines from a txt file from here: Looping through content of a file in bash

Community
  • 1
  • 1
eternalmothra
  • 221
  • 3
  • 12
  • 1
    You overwrite the file for each hit. If you use `>>` instead of `>`, it should work but is a bit inefficient (which is not a real problem with those small numbers). – flaschenpost Aug 30 '15 at 20:53
  • That works @flaschenpost. I cannot upvote comments (only have 11 reputation, down from my max at 13 for this question). If you would like to add it as an answer then I will accept it as correct (I think I can do that even with such low reputation). – eternalmothra Aug 30 '15 at 21:25
  • Of course, the proper solution is `xargs -a names.txt cp -t foldername` (or `mv -t` if you really want to move, but you say you'd prefer to copy). In particular, you [should not use `ls` in scripts](http://mywiki.wooledge.org/ParsingLs). A somewhat less convoluted loop than your original would be `while read -r file; do cp "$file" foldername; done – tripleee Aug 31 '15 at 05:17
  • @tripleee I tried this xargs -a names.txt cp -t foldername but I got this message: cp: cannot stat ‘username\r’: No such file or directory – eternalmothra Aug 31 '15 at 15:34
  • But I tried it again with file_names.txt and it worked. My understanding would be because names.txt has "username" and the file name is actually "username@mail.com.pdf". I guess I don't see how to do it without the temporary file. – eternalmothra Aug 31 '15 at 15:42
  • The error message indicates that you have DOS line feeds in at least one of your files. Don't use a Windows editor, or take care to save with Unix line feeds always. This is an extremely frequent and tiresome newbie problem; the [`bash` tag wiki](http://stackoverflow.com/tags/bash/info) devotes a long section to this topic. – tripleee Aug 31 '15 at 16:26
  • It's not clear from your question that the first file has substrings of filenames you want to match. Then my suggestion above won't work without adaptations. – tripleee Aug 31 '15 at 16:28

2 Answers2

1

Solution from comment: Your problem is just, that echo a > b is overwriting file, while echo a >> b is appending to file, so replace

ls | grep $p > file_names.txt

with

ls | grep $p >> file_names.txt

There might be more efficient solutions if the task runs everyday, but for a one-shot of 300 files your script is good.

flaschenpost
  • 2,205
  • 1
  • 14
  • 29
1

Assuming you don't have file names with newlines in them (in which case your original approach would not have a chance of working anyway), try this.

printf '%s\n' * | grep -f names.txt | xargs cp -t foldername

The printf is necessary to work around the various issues with ls; passing the list of all the file names to grep in one go produces a list of all the matches, one per line; and passing that to xargs cp performs the copying. (To move instead of copy, use mv instead of cp, obviously; both support the -t option so as to make it convenient to run them under xargs.) The function of xargs is to convert standard input into arguments to the program you run as the argument to xargs.

tripleee
  • 175,061
  • 34
  • 275
  • 318