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
2
  • 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

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).

6
  • 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
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
1
  • 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
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.

2

You must log in to answer this question.

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