Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursively move files of certain type and keep their directory structure

I have a directory which contains multiple sub-directories with mov and jpg files.

/dir/
  /subdir-a/  # contains a-1.jpg, a-2.jpg, a-1.mov
  /subdir-b/  # contains b-1.mov
  /subdir-c/  # contains c-1.jpg
  /subdir-d/  # contains d-1.mov
  ...         # more directories with the same pattern

I need to find a way using command-line tools (on Mac OSX, ideally) to move all the mov files to a new location. However, one requirement is to keep directory structure i.e.:

/dir/
  /subdir-a/  # contains a-1.mov
  /subdir-b/  # contains b-1.mov
              # NOTE: subdir-c isn't copied because it doesn't have mov files 
  /subdir-d/  # contains d-1.mov
  ...

I am familiar with find, grep, and xargs but wasn't sure how to solve this issue. Thank you very much beforehand!

like image 202
moey Avatar asked Jan 10 '12 03:01

moey


People also ask

How can you copy a directory recursively with all of its contents including sub directories )?

In order to copy the content of a directory recursively, you have to use the “cp” command with the “-R” option and specify the source directory followed by a wildcard character.

What is a recursive directory listing?

What is a recursive listing of files? Recursive means that Linux or Unix command works with the contains of directories, and if a directory has subdirectories and files, the command works on those files too (recursively).

How do I move a directory recursively in Python?

shutil. move() method Recursively moves a file or directory (source) to another location (destination) and returns the destination. If the destination directory already exists then src is moved inside that directory. If the destination already exists but is not a directory then it may be overwritten depending on os.


5 Answers

It depends slightly on your O/S and, more particularly, on the facilities in your version of tar and whether you have the command cpio. It also depends a bit on whether you have newlines (in particular) in your file names; most people don't.

Option #1

cd /old-dir
find . -name '*.mov' -print | cpio -pvdumB /new-dir

Option #2

find . -name '*.mov' -print | tar -c -f - -T - |
(cd /new-dir; tar -xf -)

The cpio command has a pass-through (copy) mode which does exactly what you want given a list of file names, one per line, on its standard input.

Some versions of the tar command have an option to read the list of file names, one per line, from standard input; on MacOS X, that option is -T - (where the lone - means 'standard input'). For the first tar command, the option -f - means (in the context of writing an archive with -c, write to standard output); in the second tar command, the -x option means that the -f - means 'read from standard input'.

There may be other options; look at the manual page or help output of tar rather carefully.

This process copies the files rather than moving them. The second half of the operation would be:

find . -name '*.mov' -exec rm -f {} +
like image 156
Jonathan Leffler Avatar answered Oct 27 '22 22:10

Jonathan Leffler


ASSERT: No files have newline characters in them. Spaces, however, are AOK.

# TEST FIRST: CREATION OF FOLDERS
find . -type f -iname \*.mov -printf '%h\n' | sort | uniq | xargs -n 1 -d '\n' -I '{}' echo mkdir -vp "/TARGET_FOLDER_ROOT/{}"

# EXECUTE CREATION OF EMPTY TARGET FOLDERS
find . -type f -iname \*.mov -printf '%h\n' | sort | uniq | xargs -n 1 -d '\n' -I '{}' mkdir -vp "/TARGET_FOLDER_ROOT/{}"

# TEST FIRST: REVIEW FILES TO BE MOVED
find . -type f -iname \*.mov -exec echo mv {} /TARGET_FOLDER_ROOT/{} \;

# EXECUTE MOVE FILES
find . -type f -iname \*.mov -exec mv {} /TARGET_FOLDER_ROOT/{} \;
like image 42
bshensky Avatar answered Oct 27 '22 22:10

bshensky


Being large files, if they are on the same file system you don't want to copy them, but just to replicate their directory structure while moving. You can use this function:

# moves a file (or folder) preserving its folder structure (relative to source path)
# usage: move_keep_path source destination
move_keep_path () {
  # create directories up to one level up
  mkdir -p "`dirname "$2"`"
  mv "$1" "$2"
}

Or, adding support to merging existing directories:

# moves a file (or folder) preserving its folder structure (relative to source path)
# usage: move_keep_path source destination
move_keep_path () {
  # create directories up to one level up
  mkdir -p "`dirname "$2"`"
  if [[ -d "$1" && -d "$2" ]]; then
    # merge existing folder
    find "$1" -depth 1 | while read file; do
      # call recursively for all files inside
      mv_merge "$file" "$2/`basename "$file"`"
    done
    # remove after merge
    rmdir "$1"
  else
    # either file or non-existing folder
    mv "$1" "$2"
  fi
}
like image 42
djjeck Avatar answered Oct 27 '22 22:10

djjeck


It is easier to just copy the files like:

cp --parents some/folder/*/*.mov new_folder/
like image 24
robodo Avatar answered Oct 27 '22 21:10

robodo


from the parent directory of "dir execute this:

find ./dir -name "*.mov" | xargs tar cif mov.tar

Then cd to the directory you want to move the files to and execute this:

tar xvf /path/to/parent/directory/of"dir"/mov.tar
like image 32
ennuikiller Avatar answered Oct 27 '22 23:10

ennuikiller