1

I need to copy the files with certain patterns. I need to perform the non recursive copy on a given directory using shutil. I tried this code given below with performs recursive copy. Is there any options to specify in it to do the non recursive copy.

    from fnmatch import fnmatch, filter
    from os.path import isdir, join
    from shutil import copytree, ignore_patterns

    src_directory = r'PATH'
    dst_directory = r'PATH'
    copytree(src_directory, ignored_directory,ignore=ignore_patterns('*.txt'))
ArockiaRaj
  • 588
  • 4
  • 17
  • What do you mean by non recursive? Only files from a directory or both files and directories but nothing under those directories? – Sushant Aug 29 '18 at 10:15
  • only files from a directory not folders @ThatBird – ArockiaRaj Aug 29 '18 at 10:16
  • Possible duplicate of [python copy files by wildcards](https://stackoverflow.com/questions/18371768/python-copy-files-by-wildcards) – Peter Wood Aug 29 '18 at 10:18
  • Any solution in which you copy files by wildcards leaves out many nice features of copytree, such as symlink handling, copying the file metadata, etc. [This solution](https://stackoverflow.com/a/66941880/2394287) creates a truly non-recursive version of copytree – Michael Krebs Apr 04 '21 at 13:47

3 Answers3

2

Make sure the destination directory exists first. Then create a list of all the files to ignore with glob.glob. Then iterate over all the file and directories in the src directory and copy each file to the dst directory if it is a file and it is not in the ignore list:

import os, shutil, glob
src = 'my_dir'
dst = 'my_dir_cp'

try:
    os.mkdir(dst)
except FileExistsError:
    pass

ignore = glob.glob(os.path.join(src, '*.txt'))
for file in os.listdir(src):
    file = os.path.join(src, file)
    if file not in ignore and os.path.isfile(file):
        shutil.copy(file, dst)

A full example:

$ ls my_dir
bob  cat.txt  fish  jim
$ python -q
>>> import os, shutil, glob
>>> src = 'my_dir'
>>> dst = 'my_dir_cp'
>>> 
>>> try:
...     os.mkdir(dst)
... except FileExistsError:
...     pass
... 
>>> ignore = glob.glob(os.path.join(src, '*.txt'))
>>> for file in os.listdir(src):
...     file = os.path.join(src, file)
...     if file not in ignore and os.path.isfile(file):
...         shutil.copy(file, dst)
... 
'my_dir_cp/jim'
'my_dir_cp/bob'
>>> 
$ ls my_dir_cp
bob  jim

If you want to be able to ignore multiple glob patterns, then you can glob.glob each of them and concatenate the results together (in a list-comprehension):

import os, shutil, glob
src = 'my_dir'
dst = 'my_dir_cp'

try:
    os.mkdir(dst)
except FileExistsError:
    pass

patterns_to_ignore = ['*.txt', '*.bat']
ignore = [e for p in patterns_to_ignore for e in glob.glob(os.path.join(src, p))]
for file in os.listdir(src):
    file = os.path.join(src, file)
    if file not in ignore and os.path.isfile(file):
        shutil.copy(file, dst)
Joe Iddon
  • 20,101
  • 7
  • 33
  • 54
1

This is more of a hack but will copy all the files in a directory to destination directory non recursively. You could use glob or regex to ignore files -

import re

def copytree(src, dst):
     for item in [f for f in os.listdir(src) if os.path.isfile(os.path.join(src, f)) if not re.match(f, '[.]txt$')]:
         s = os.path.join(src, item)
         d = os.path.join(dst, item)
         shutil.copy2(s, d)
Sushant
  • 3,499
  • 3
  • 17
  • 34
0

As you have chosen to use copytree from shutil, be aware that any solution in which you loop over the files to copy each one leaves out many nice features of copytree, such as symlink handling, copying the file metadata, etc.

You can keep all the extras by creating a non-recursive copytree called copyfiles. copyfiles works identically to copytree, except that it extends the ignore parameter to also ignore folders while copying.

import shutil, os

def copyfiles(src, dst, ignore=lambda a, b: set(), **kwargs):
    return shutil.copytree(src, dst, ignore=lambda s, names: set(ignore(s, names)).union(set(filter(lambda name: os.path.isdir(os.path.join(s, name)), names))), **kwargs)
# use copyfiles like so:

copyfiles(src_directory, dst_directory, ignore=shutil.ignore_patterns('*.txt')) # copies non-recursively
Michael Krebs
  • 1,212
  • 10
  • 11