if anyone can help me that would be much appreciated. I may be in the wrong place for this but it's a major roadblock for my research.

I have a folder with about 1700 folders. I have a list of numbers in excel which correlates to a subset of these folders which I am interested in (about 1300). The folder labels contain(but are not the same as) these numbers from excel. I need to copy 1 out of the 2 files in each folder(out of the 1700). The file I need to copy contains a unique ending _dc_brain_FA.nii (compared to the other file in each folder). I would like to copy these files into a single folder containing the 1300 or so _dc_brain_FA.nii* files.

  • 1
    My course of action would be to export the file into CSV, then read it into Emacs, then copy the files matching the records in CSV file. This can be done with or without Emacs, by say, something like cat my.csv | awk '{ print $1 }' | xargs -I{} cp /my/old/directory/{}_dc_barin_FA.nii /my/new/directory/{}_dc_brain_FA.nii (note that I'd need to see the format to figure out what $1 actually need to be and to know the path to the directory where the files are stored. – wvxvw Mar 22 '16 at 17:01
  • 4
    If you're an Emacs beginner, you might want to try another tool for this job. Emacs can do it, but a scripting language like Bash is a more natural fit for this kind of problem. – Tyler Mar 22 '16 at 20:39

The defun-form below defines a new interactive command that does the job. If you need this functionality more often you can put this defun-form into your initialization file ~/.emacs. If you need it only once you can put it into your *scratch* buffer place point behind the last closing parenthesis and evaluate it with the key sequence C-x C-e. Note, this evaluates just the definition but not the command itself.

The first argument of defun is the name of the newly defined command -- James-move-files in our case. You can call this command with M-x James-move-files RET. The interactive form in the command definition causes that the command interactively asks you for all the needed arguments of the command.

The second argument of James-move-files is the list of the arguments of this command. The third argument is the comment string. This comment string is presented to you when you type C-h f James-move-files RET. You can use the key sequence C-h f on any of the keywords in the definition of James-move-files to learn more about its functionality. There is more detailed help in the elisp-info files C-h i m elisp RET. You can search that manual with the incremental search C-s foo. Even if tells you Failing search: ... [end of node] press C-s again.

I hope that the help string of James-move-files is good enough for you to use this function.

(defun James-move-files (pattern-file path-with-folders folder-name-pattern file-name-pattern target-dir)
  "Copy files matching a regular expression from a series of folders from a given path. The folders are identified by IDs collected from a pattern file.
The arguments of this command are:

PATTERN-FILE: Ascii file containing IDs. Separator
may be spaces, form feeds, tabs, line feeds, carry returns, or
vertical tabs

PATH-WITH-FOLDERS: Path where to find the folders with names marked by the IDs.

FOLDER-NAME-PATTERN: Regular expression matching a folder name with ID as first group. You can use \\([0-9]+\\) as group part of the regular expression if you want to match numbers.

FILE-NAME-PATTERN: Regular expression that matches the file to copy from the folder. Example: .*_dc_brain_FA\\.nii

TARGET-DIR: Target directory for the files to copy.
  (interactive "fPattern file:\nDPath with folders:\nsFolder name pattern:\nsFile name pattern:\nDTarget directory:")
  (with-current-buffer (find-file pattern-file)
    (let* ((list-of-patterns (split-string (buffer-string) nil t))
       (path (directory-file-name path-with-folders))) ; without trailing slash /
      (mapc (lambda (folder)
          (when (and (file-directory-p folder)
             (string-match folder-name-pattern folder)
             (member (match-string 1 folder) list-of-patterns))
        (mapc (lambda (file)
            (copy-file file target-dir))
              (directory-files (concat path "/" folder) t file-name-pattern))))
        (directory-files path)))))

Like wvxvw and Tyler, I'd use other tools for this.

I think the following shell command would do the trick:

find SOURCEDIR -name "*_dc_brain_FA.nii" | grep -f PATTERNFILE | xargs -d "\n" cp -t TARGETDIR

where SOURCEDIR is where the files are now; TARGETDIR is the directory you want the files to end up in; and PATTERNFILE is the name of a file containing the patterns to grep for (one per line) derived from the csv file.

You would, of course, need to ensure that each pattern can match only one of the files; so the patterns should probably not just be numbers! (Perhaps the numbers wrapped by underscores would be suitable?)

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.