I have a directory (let's call it "Movies") which contains many files and folders. I have a long list of file names in a .csv file (around 4000 entries) which refer to files which are located somewhere within the Movies directory sub-folders.

How can I search the Movies directory recursively for the files listed in the .csv and copy them to a separate directory ("Sorted_Media")?

EDIT: Hi, I have attached an example section of the csv. There are two columns of data (from a spreadsheet), which are separated by a comma delimiter in the .csv. The first colum of file names are the ones that I need to search (i.e. NOT the KA* file names). Some of the file names do have spaces so this is something which need to be considered as someone else pointed out.

preservation stocklshots - 16ln916-963.mp4,KA0003773-002.mp4
Preservation Stockshots_ 16LN916-963.mp4,KA0003773-001.mp4
Preservation Stockshots_16LN679-738.mp4,KA0003775-002.mp4
Preservation Stockshots_16LN01-52.mp4,KA0003776-002.mp4
Preservation Stockshots_LN566-LN624.mp4,KA0004507-001.mp4
Preservation Stockshots_LN675-LN705.mp4,KA0004508-001.mp4
Preservation Stockshots_LN706-752.mp4,KA0004509-001.mp4
Preservation Stockshots_LN930-LN972.mp4,KA0004511-001.mp4
Preservation Stockshots_LN1023-LN1059.mp4,KA0004513-001.mp4
Preservation Stockshots_LN1152-LN1220.mp4,KA0004515-001.mp4
Preservation Stockshots_16LN320-379.mp4,KA0004517-001.mp4
  • I assume the csv only includes the file's names, not the paths? Commented Feb 25, 2016 at 18:35
  • updated my original post with this info.
    – neilH
    Commented Feb 26, 2016 at 12:54

3 Answers 3

while IFS=, read -r file rest
  find /path/to/movies_dir -name "${file}" -exec cp '{}' /path/to/Sorted_Media/ \;
done < mylist.csv

That assumes file names don't contain wildcard characters (?, [, * or backslash).

  • yes, @StéphaneChazelas is right.. please see the modified code above
    – MelBurslan
    Commented Feb 25, 2016 at 17:58
  • Presumably the reason the list is in a CSV file is that there are values ("V") separated ("S") by commas ("C"). A CSV file with only one column of data would be called a "list" (:
    – DopeGhoti
    Commented Feb 25, 2016 at 17:58
  • Unless the original poster provides a sample, my assumption is a single column csv, i.e. a list file.
    – MelBurslan
    Commented Feb 25, 2016 at 18:01
  • updated my original post with this info.
    – neilH
    Commented Feb 26, 2016 at 12:54
  • The csv contains two separate value 'columns' separate by a comma. the first value is the filename which I am interested in (I've added an example to my initial query).
    – neilH
    Commented Feb 26, 2016 at 14:16

Let us say that, for example, the CSV looks like this:

Star Wars IV: A New Hope,/mnt/Movies/SciFi/starwars-4.avi

You can then do something like:

for file in $( cut -d, -f 2 /path/to/movielist.csv ); do
    cp "$file" $DEST/"${file##*/}"
  • Hello, no sorry, the csv doesn't contain file locations. The files are in subfolders under one main known folder so the script would have to recursively search for each file name without a specific address.
    – neilH
    Commented Feb 26, 2016 at 14:13

With zsh, you could do:

files=(${(f)"$(cut -d , -f 1 <movies.csv)"})
eval "tocopy=(Movies/**/(${(j:|:)glob})(D.))"
print -rl -- $tocopy # to  check it's OK
cp -i -- $tocopy Sorted_Media/


  • cut -d , -f1 < movies.csv: retrieve the first column of the csv (one file name per line).
  • ${(f)"$(cmd)"}: split the output of cmd on line feeds ($files is now an array with all the file names).
  • glob=('$files['{1..$#files}']'): make another array with elements literally $files[1], $files[2]...
  • ${(j:|:)array}: join the elements of the array with |, so we're basically evaluating the tocopy=(Movies/**/($files[1]|$files[2]|...|$files[n])(DN.)) command (where n is the number of elements in the $files array).
  • **/: recursive globbing (search for the files in any level of subdirectories).
  • (D.): glob qualifiers (search in hidden dirs as well (D), only regular files (.))

Or with GNU tools:

find Movies -type f -print0 |
  awk '
    !d {f[$1]; next}
    $NF in f {
      delete f[$NF]
    }' FS=, file.csv d=1 FS=/ RS='\0' ORS='\0' - |
  xargs -r0 cp -vt Sorted_Media/

By using delete, we make sure only the first instance (if there are several files with the same name) is copied. -i in the zsh solution is to give you a chance to decide what to do if there's a conflict. We can't use -i here because cp's stdin is no longer the terminal.

Replace cp with echo cp if you just want to check what it would be doing without actually doing it.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .