2

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
PreservationStockshots_16LN679_738.mp4,KA0003775-001.mp4
Preservation Stockshots_16LN01-52.mp4,KA0003776-002.mp4
Preservation_Stockshots_16LN01_52.mp4,KA0003776-001.mp4
Preservation Stockshots_LN566-LN624.mp4,KA0004507-001.mp4
PreservationStockShots_LN566_LN624.mp4,KA0004507-002.mp4
Preservation Stockshots_LN675-LN705.mp4,KA0004508-001.mp4
PreservationStockshots_LN675_LN705.mp4,KA0004508-002.mp4
Preservation Stockshots_LN706-752.mp4,KA0004509-001.mp4
PreservationStockshots_LN706_LN752.mp4,KA0004509-002.mp4
Preservation Stockshots_LN930-LN972.mp4,KA0004511-001.mp4
PreservationStockShots_LN930_LN972.mp4,KA0004511-002.mp4
Preservation Stockshots_LN1023-LN1059.mp4,KA0004513-001.mp4
PreservationStockShots_LN1023_LN1059.mp4,KA0004513-002.mp4
Preservation Stockshots_LN1152-LN1220.mp4,KA0004515-001.mp4
PreservationStockShots_LN1152_LN1220.mp4,KA0004515-002.mp4
Preservation Stockshots_16LN320-379.mp4,KA0004517-001.mp4
Preservation_Stockshots_16LN320_379.mp4,KA0004517-002.mp4
  • I assume the csv only includes the file's names, not the paths? – Jacob Vlijm Feb 25 '16 at 18:35
  • updated my original post with this info. – neilH Feb 26 '16 at 12:54
2
while IFS=, read -r file rest
do
  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 Feb 25 '16 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 Feb 25 '16 at 17:58
  • Unless the original poster provides a sample, my assumption is a single column csv, i.e. a list file. – MelBurslan Feb 25 '16 at 18:01
  • updated my original post with this info. – neilH Feb 26 '16 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 Feb 26 '16 at 14:16
1

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

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

You can then do something like:

#!/bin/bash
DEST=/path/to/SortedMedia
for file in $( cut -d, -f 2 /path/to/movielist.csv ); do
    cp "$file" $DEST/"${file##*/}"
done
  • 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 Feb 26 '16 at 14:13
1

With zsh, you could do:

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

Explained:

  • 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]
      print
    }' 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.