Copy specific subdirectories by its name with Python - python

If I have a folder which has multiple subfolders, each has same structure sub1, sub2 and sub3:
├─a
│ ├─sub1
│ ├─sub2
│ └─sub3
├─b
│ ├─sub1
│ ├─sub2
│ └─sub3
└─c
├─sub1
├─sub2
└─sub3
...
I want to copy sub1 and sub2 if there are image files inside and ignore other subfolders (sub3...) while keeping tree structure of directory. This is expected result:
├─a
│ ├─sub1
│ └─sub2
├─b
│ ├─sub1
│ └─sub2
└─c
├─sub1
└─sub2
...
What should I do to get expected result? I have try following code but it only copy sub1, sub2 and sub3, I got TypeError: copy() takes no arguments (1 given).
Thanks to #PrasPJ.
import os
import os.path
import shutil
import fnmatch
src_dir= ['C:/Users/User/Desktop/source/input'] # List of source dirs
included_subdirs = ['sub1', 'sub2'] # subdir to include from copy
dst_dir = 'C:/Users/User/Desktop/source/output' # folder for the destination of the copy
for root_path in src_dir:
#print(root_path)
for root, dirs, files in os.walk(root_path): # recurse walking
for dir in included_subdirs:
#print(dir)
if dir in dirs:
source = os.path.join(root,dir)
f_dst = source.replace(root_path, dst_dir)
print (source, f_dst)
for file in os.listdir(source):
print(file)
if file.endswith(".jpg") or file.endswith(".jpeg"):
shutil.copytree(source, f_dst)
Reference related:
https://www.daniweb.com/programming/software-development/threads/356380/how-to-copy-folders-directories-using-python
Python shutil copytree: use ignore function to keep specific files types
How to copy contents of a subdirectory in python

Hi you are trying to use dictionary's copy function which is wrong
import os
import os.path
import shutil
import fnmatch
list_of_dirs_to_copy = ['C:/Users/User/Desktop/test/input'] # List of source dirs
included_subdirs = ['sub1', 'sub2'] # subdir to include from copy
dest_dir = 'C:/Users/User/Desktop/test/output' # folder for the destination of the copy
for root_path in list_of_dirs_to_copy:
print(root_path)
for root, dirs, files in os.walk(root_path): # recurse walking
for dir in included_subdirs:
print(dir)
if dir in dirs:
source = os.path.join(root,dir)
f_dest = source.replace(root_path, dest_dir)
print source, f_dest
shutil.copytree(source,f_dest)

import os
import os.path
import shutil
import fnmatch
list_of_dirs_to_copy = ['C:/Users/User/Desktop/test/input'] # List of source dirs
included_subdirs = ['sub1', 'sub2'] # subdir to include from copy
dest_dir = 'C:/Users/User/Desktop/test/output' # folder for the destination of the copy
for root_path in list_of_dirs_to_copy:
print(root_path)
for root, dirs, files in os.walk(root_path): # recurse walking
for dir in included_subdirs:
print(dir)
if dir in dirs:
source = os.path.join(root,dir)
for f in os.listdir(source):
print os.path.join(source,f)
if os.path.isfile(os.path.join(source,f)) and (f.endswith("jpg") or f.endswith("jpeg")):
f_dest = source.replace(root_path, dest_dir)
print source, f_dest
shutil.copytree(source,f_dest)
break # found any one matching file

Here is the update
for root_path in list_of_dirs_to_copy:
for root, dirs, files in os.walk(root_path): # recurse walking
path_to_copy = {}
for dir in included_subdirs:
if dir in dirs:
source = os.path.join(root,dir)
for f in os.listdir(source):
if os.path.isfile(os.path.join(source,f)) and (f.endswith("jpg") or f.endswith("jpeg")):
f_dest = source.replace(root_path, dest_dir)
path_to_copy.update({source: f_dest})
if len(path_to_copy) == len(included_subdirs):
for source, f_dest in path_to_copy.iteritems():
shutil.copytree(source,f_dest)

Related

Name of files is not changed when python script is run

I was creating a simple python script that iterates through files in a directory and changes the names of the files based on a num variable that increases by one after each iteration.
Here is the code used, with the directory and files involved.
import os
directory = 'JAN'
num = 1
for filename in os.listdir(directory):
f = os.path.join(directory, filename)
new_f = f'JAN-{num}.txt'.format(num=num)
if os.path.isfile(filename):
os.rename(filename, new_f)
num += 1
print(new_f)
Current Files
├── _JAN
│ ├── first.txt
│ ├── second.txt
│ ├── third.txt
│ └── fourth.txt
Desired Changes to Files
├── _JAN
│ ├── JAN-1.txt
│ ├── JAN-2.txt
│ ├── JAN-3.txt
│ └── JAN-4.txt
I have tried a few iterations and the only output received is each new file name printed to the console with no changes to the file names within the JAN directory.
Any help would be much appreciated.
Like the comments say, you need to check for f not filename and you also need to save it to the dir:
import os
directory = 'JAN'
num = 1
for filename in os.listdir(directory):
f = os.path.join(directory, filename)
new_f = os.path.join(directory, f'JAN-{num}.txt'.format(num=num))
if os.path.isfile(f):
os.rename(f, new_f)
num += 1
print(new_f)
You can also use enumerate instead of num:
for i, filename in enumerate(os.listdir(directory), 1):
f = os.path.join(directory, filename)
new_f = os.path.join(directory, f'JAN-{i}.txt')
if os.path.isfile(f):
os.rename(f, new_f)
print(new_f)
additional note: there is no need to use .format when you use f'' already.
import os
directory = 'JAN'
num = 1
os.chdir(directory) # Changes current working directory to "JAN"
for filename in os.listdir(): # equals to: for filename in current directory.
new_f = f'{directory}-{num}.txt'
if os.path.isfile(filename): # now it can detect filename since we've changed the current working directory.
os.rename(filename, new_f)
num += 1
Have a look to os.scandir. It's an iterator whose object are os.DirEntry instances which make them easy to work with (have attributes such as name, path, is_file, ...), in particular you can bypass the side-effect of listdir that returns basenames and not paths.
Here assumed that the files are already in the right order in the directory.
import os
directory = 'JAN'
new_f_template = os.path.join(directory, 'JAN-{}.txt')
with os.scandir(directory) as files:
for i, f in enumerate(files, 1):
if f.is_file():
new_f = new_f_template.format(i)
os.rename(f.path, new_f)
print(f"{f.name} -> {new_f}")

Copying batch image files from sub-folders to parent folders

In one directory there are several folders that their names are as follows: 301, 302, ..., 600.
Each of these folders contain two folders with the name of A and B. I need to copy all the image files from A folders of each parent folder to the environment of that folder (copying images files from e.g. 600>A to 600 folder) and afterwards removing A and B folders of each parent folder. I found the solution from this post but I don't know how to copy the files into parent folders instead of sub-folder and also how to delete the sub-folders after copying and doing it for several folders.
import shutil
import os, sys
exepath = sys.argv[0]
directory = os.path.dirname(os.path.abspath(exepath))+"\\Files\\"
credit_folder = os.path.dirname(os.path.abspath(exepath))+"\\Credits\\"
os.chdir(credit_folder)
os.chdir(directory)
Source = credit_folder
Target = directory
files = os.listdir(Source)
folders = os.listdir(Target)
for file in files:
SourceCredits = os.path.join(Source,file)
for folder in folders:
TargetFolder = os.path.join(Target,folder)
shutil.copy2(SourceCredits, TargetFolder)
print(" \n ===> Credits Copy & Paste Sucessfully <=== \n ")
#hellohawii gave an excellent answer. Following code also works and you only need change value of Source when using.
import shutil
import os, sys
from tqdm import tqdm
exepath = sys.argv[0] # current path of code
Source = os.path.dirname(os.path.abspath(exepath))+"\\Credits\\" # path of folders:301, 302... 600
# Source = your_path_of_folders
files = os.listdir(Source) # get list of folders under 301 etc, in your situation: [A, B]
def get_parent_dir(path=None, offset=-1):
"""get parent dir of current path"""
result = path if path else __file__
for i in range(abs(offset)):
result = os.path.dirname(result)
return result
def del_files0(dir_path):
"""delete full folder"""
shutil.rmtree(dir_path)
for file_path in files:
current_path = os.path.join(Source, file_path) # current_path
if file_path == 'A': # select the folder to copy
file_list = os.listdir(current_path) # get file_list of selected folder
parent_path = get_parent_dir(current_path) # get parent dir path, namely target path
for file in tqdm(file_list):
shutil.copy(file, parent_path)
del_files0(current_path) # delete current path(folder)
print(" \n ===> Credits Copy & Paste & delete Successfully <=== \n ")
I recommend you to use the Pathlib.
from pathlib import Path
import shutil
from tqdm import tqdm
folder_to_be_sorted = Path("/your/path/to/the/folder")
for folder_named_number_i in tqdm(list(folder_to_be_sorted.iterdir())):
# folder_named_number_i is 301, 302, ..., 600
A_folder = folder_named_number_i / "A"
B_folder = folder_named_number_i / "B"
# move files
for image_i in A_folder.iterdir():
shutil.move(str(image_i), folder_named_number_i)
# remove directories
shutil.rmtree(str(A_folder))
shutil.rmtree(str(B_folder))
The os.path is a more low-level module. I post another version here since you are using the os module in your question.
import shutil
import os
from tqdm import tqdm
folder_to_be_sorted = "/your/path/to/the/folder"
for folder_named_number_name in tqdm(os.listdir(folder_to_be_sorted)):
folder_named_number_i = os.path.join(folder_to_be_sorted, folder_named_number_name)
# folder_named_number_i is 301, 302, ..., 600
A_folder = os.path.join(folder_named_number_i, "A")
B_folder = os.path.join(folder_named_number_i, "B")
# move files
for image_i_name in os.listdir(A_folder):
image_i = os.path.join(A_folder, image_i_name)
shutil.move(str(image_i), folder_named_number_i)
# remove directories
shutil.rmtree(str(A_folder))
shutil.rmtree(str(B_folder))
By the codes above I suppose you want to transfrom
# /your/path/to/the/folder
# │
# └───301
# │ │
# │ └───A
# │ │ └───image_301_A_1.png
# │ │ └───image_301_A_2.png
# │ │ └───image_301_A_3.png
# │ │ └───...(other images)
# │ │
# │ └───B
# │ └───image_301_B_1.png
# │ └───image_301_B_2.png
# │ └───image_301_B_3.png
# │ └───...(other images)
# │
# └───302(like 301)
# :
# :
# └───600(like 301)
to:
# /your/path/to/the/folder
# │
# └───301
# │ │
# │ └───image_301_A_1.png
# │ └───image_301_A_2.png
# │ └───image_301_A_3.png
# │ └───...(other images in folder 301/A/)
# │
# └───302(like 301)
# :
# :
# └───600(like 301)

Copy folders and subfolders, but only first files in subfolders with python

I have a file structure containing folders, subfolders, subsubfolders a.s.o. Only the very last subsubfolders contain files. I would like to copy the file structure, while not copying all the files, but only the first file (or just one file) from each of the subsubfolders. I noticed that shutil.copytree(src, dst) can do something similar, but I don't know how to restrict it to copy only the first file from the subsubfolders. Thanks for advice on how to accomplish this!
My file structure:
folder1
subfolder11
subsubfolder111
file1
file2
file3...
folder2
sulfolder21
file1
file2
file3...
Desired structure:
folder1
subfolder11
subsubfolder111
file1
folder2
sulfolder21
file1
I don't know if you can customize that much copytree, but with os.walk and parsing the folders you can do it, here is an example:
import os
import shutil
p1 = r"C:\src_dir"
p2 = r"C:\target_dir"
for path, folders, files in os.walk(p1):
if not files: continue
src = os.path.join(path, files[0])
dst_path = path.replace(p1, '') + os.sep
dst_folder = p2 + dst_path
# create the target dir if doesn't exist
if not os.path.exists(dst_folder):
os.makedirs(dst_folder)
# create dst file with only the first file
dst = p2 + dst_path + files[0]
# copy the file
shutil.copy2(src, dst)
Based on GuillaumeJ's answer one can generalize the copying to N files:
# limit of files to copy
N=3
for path, folders, files in os.walk(p1):
# you might want to sort files first before executing the below
for file_ in files[:N]:
# if not files: continue
src = os.path.join(path, file_)
dst_path = path.replace(p1, '') + os.sep
dst_folder = p2 + dst_path
# create the target dir if doesn't exist
if not os.path.exists(dst_folder):
os.makedirs(dst_folder)
# create dst file with only the first file
dst = p2 + dst_path + file_
# copy the file
shutil.copy2(src, dst)

Copy files from multiple specific subfolders

Under the file path D:/src, I have images folders and its subfolders which have a regular structure as follows:
Folder A
- Subfolder a
- Subfolder b
- Subfolder c
Folder B
- Subfolder a
- Subfolder b
- Subfolder c
- Subfolder d
Folder C
- Subfolder a
- Subfolder b
- Subfolder c
...
I want to copy all .jpg files in Subfolder b from Folder A, B, C and so on to a new folder Subfolder b in D:/dst. How can I do it in Python? Thanks.
Subfolder b
-xxx.jpg
-xyx.jpg
-yxz.jpg
...
Here is what I have found from the following link may helps:
Copy certain files from one folder to another using python
import os;
import shutil;
import glob;
source="C:/Users/X/Pictures/test/Z.jpg"
dest="C:/Users/Public/Image"
if os.path.exists(dest):
print("this folder exit in this dir")
else:
dir = os.mkdir(dest)
for file in glob._iglob(os.path.join(source),""):
shutil.copy(file,dest)
print("done")
Try this
import os
from os.path import join, isfile
BASE_PATH = 'd:/test'
SUBFOLDER = 'Subfolder b'
for folder, subfolders, *_ in os.walk(BASE_PATH):
if SUBFOLDER in subfolders:
full_path = join(BASE_PATH, folder, SUBFOLDER)
files = [f for f in os.listdir(full_path) if isfile(join(full_path, f)) and f.lower().endswith(('.jpg', '.jpeg'))]
for f in files:
file_path = join(full_path, f)
print (f'Copy {f} somewehere')
Assuming you have 2 levels of nesting
root_dir = './data'
dest_dir = './new_location'
os.listdir(root_dir)
for folder in os.listdir(root_dir):
folder_path = os.path.join(root_dir, folder)
if os.path.isdir(folder_path):
for subfolder in os.listdir(folder_path):
if subfolder == 'Subfolder B':
subfolder_path = os.path.join(root_dir, folder, subfolder)
print(subfolder_path)
for filename in os.listdir(subfolder_path):
file_path = os.path.join(root_dir, folder, subfolder, filename)
dest_path = os.path.join(dest_dir, filename)
shutil.copy(file_path, dest_path)
print("Copied ", file_path, "to", dest_path)
You just need 2 for loops, and in the the inner for loop you just check whether the folder name matches Subfolder B. If it does then copy all the files inside that directory to your destination folder.
Here's a short script that should do the work...
import os
# list all the directories in current directory
dirs = [x[0] for x in os.walk("D:/src")]
for d in dirs:
## list all files in A/b/*, B/b/*, C/b/*...
files_to_copy = os.listdir(os.path.join(d, "b"))
for f in files_to_copy:
if f.endswith(".jpg"): ## copy the relevant files to dest
shutil.copy(os.path.join(d, "b", f), os.path.join(dest, f))
import shutil
import os
def move_files(source_director, destination_director):
# select the files in the source directory
fisiere = [fisier for fisier in os.listdir(source_director) if os.path.isfile(os.path.join(source_director, fisier))]
print(fisiere)
# each file in the source directory is moved to the destination directory
for fisier in fisiere:
shutil.move(source_director + '\\' + fisier, destination_director)
def copy_files(source_director, destination_director):
# select the files in the source directory
fisiere = [fisier for fisier in os.listdir(source_director) if os.path.isfile(os.path.join(source_director, fisier))]
print(fisiere)
# each file in the source directory is copied to the destination directory
for fisier in fisiere:
shutil.copy(source_director + '\\' + fisier, destination_director)
if __name__ == '__main__':
# WARNING: paths to directories must be written with '\\' (double backslash)
# here you set the DIN directory you want to move / copy the files
source_director = 'c:\\Folder'
destination_director = 'c:\\Folder\\Subfolder1\\Subfolder2'
#CASE 1
# move_files(source_director, destination_director)
#CASE 2
copy_files(source_director, destination_director)
THE SOURCE OF THIS CODE HERE:
https://neculaifantanaru.com/en/python-copy-or-moves-files-from-a-folder-to-another-folder.html

Print out the whole directory tree

The code I have now:
import os
Tree = {}
Tree = os.listdir('Dir')
>>> print(Tree)
['New Folder', 'Textfile1.txt', 'Textfile2.txt']
That doesn't print out the files in the subdirectories. (New Folder is a subdirectory).
My question is, how can I output all the files in the directory and the files in subdirectories?
import os
def Test1(rootDir):
list_dirs = os.walk(rootDir)
for root, dirs, files in list_dirs:
for d in dirs:
print os.path.join(root, d)
for f in files:
print os.path.join(root, f)
OR:
import os
def Test2(rootDir):
for lists in os.listdir(rootDir):
path = os.path.join(rootDir, lists)
print path
if os.path.isdir(path):
Test2(path)
For the test file tree:
E:\TEST
│--A
│ │--A-A
│ │ │--A-A-A.txt
│ │--A-B.txt
│ │--A-C
│ │ │--A-B-A.txt
│ │--A-D.txt
│--B.txt
│--C
│ │--C-A.txt
│ │--C-B.txt
│--D.txt
│--E
Running the following code:
Test1('E:\TEST')
print '======================================='
Test2('E:\TEST')
You can see there are difference between the results:
>>>
E:\TEST\A
E:\TEST\C
E:\TEST\E
E:\TEST\B.txt
E:\TEST\D.txt
E:\TEST\A\A-A
E:\TEST\A\A-C
E:\TEST\A\A-B.txt
E:\TEST\A\A-D.txt
E:\TEST\A\A-A\A-A-A.txt
E:\TEST\A\A-C\A-B-A.txt
E:\TEST\C\C-A.txt
E:\TEST\C\C-B.txt
=======================================
E:\TEST\A
E:\TEST\A\A-A
E:\TEST\A\A-A\A-A-A.txt
E:\TEST\A\A-B.txt
E:\TEST\A\A-C
E:\TEST\A\A-C\A-B-A.txt
E:\TEST\A\A-D.txt
E:\TEST\B.txt
E:\TEST\C
E:\TEST\C\C-A.txt
E:\TEST\C\C-B.txt
E:\TEST\D.txt
E:\TEST\E
>>>
To save them in a list:
import os
files = []
def Test1(rootDir):
files.append(rootDir)
list_dirs = os.walk(rootDir)
for root, dirs, files in list_dirs:
for d in dirs:
files.append(os.path.join(root, d))
for f in files:
files.append(os.path.join(root, f))
import os
files = [rootDir]
def Test2(rootDir):
for lists in os.listdir(rootDir):
path = os.path.join(rootDir, lists)
files.append(path)
if os.path.isdir(path):
Test2(path)
From recipe 577091 on the Python Cookbook, you might use or learn from the TREE Emulator there.
import sys, os
FILES = False
def main():
if len(sys.argv) > 2 and sys.argv[2].upper() == '/F':
global FILES; FILES = True
try:
tree(sys.argv[1])
except:
print('Usage: {} <directory>'.format(os.path.basename(sys.argv[0])))
def tree(path):
path = os.path.abspath(path)
dirs, files = listdir(path)[:2]
print(path)
walk(path, dirs, files)
if not dirs:
print('No subfolders exist')
def walk(root, dirs, files, prefix=''):
if FILES and files:
file_prefix = prefix + ('|' if dirs else ' ') + ' '
for name in files:
print(file_prefix + name)
print(file_prefix)
dir_prefix, walk_prefix = prefix + '+---', prefix + '| '
for pos, neg, name in enumerate2(dirs):
if neg == -1:
dir_prefix, walk_prefix = prefix + '\\---', prefix + ' '
print(dir_prefix + name)
path = os.path.join(root, name)
try:
dirs, files = listdir(path)[:2]
except:
pass
else:
walk(path, dirs, files, walk_prefix)
def listdir(path):
dirs, files, links = [], [], []
for name in os.listdir(path):
path_name = os.path.join(path, name)
if os.path.isdir(path_name):
dirs.append(name)
elif os.path.isfile(path_name):
files.append(name)
elif os.path.islink(path_name):
links.append(name)
return dirs, files, links
def enumerate2(sequence):
length = len(sequence)
for count, value in enumerate(sequence):
yield count, count - length, value
if __name__ == '__main__':
main()
Here is yet another version appropriate for python3
Example output:
pyvarstar/
|-- .bashrc
|-- README
|-- vstars -> versions/vstars_20170804/
|-- versions/
|   |-- vstars_20170804/
|   |   |-- lib/
|   |   |   |-- vstars/
|   |   |-- bin/
|   |   |   |-- getcoords
|   |   |   |-- find_burst
The code:
def realname(path, root=None):
if root is not None:
path=os.path.join(root, path)
result=os.path.basename(path)
if os.path.islink(path):
realpath=os.readlink(path)
result= '%s -> %s' % (os.path.basename(path), realpath)
return result
def ptree(startpath, depth=-1):
prefix=0
if startpath != '/':
if startpath.endswith('/'): startpath=startpath[:-1]
prefix=len(startpath)
for root, dirs, files in os.walk(startpath):
level = root[prefix:].count(os.sep)
if depth >-1 and level > depth: continue
indent=subindent =''
if level > 0:
indent = '| ' * (level-1) + '|-- '
subindent = '| ' * (level) + '|-- '
print('{}{}/'.format(indent, realname(root)))
# print dir only if symbolic link; otherwise, will be printed as root
for d in dirs:
if os.path.islink(os.path.join(root, d)):
print('{}{}'.format(subindent, realname(d, root=root)))
for f in files:
print('{}{}'.format(subindent, realname(f, root=root)))
Use os.walk:
>>> import os
>>> print(os.walk.__doc__)
Directory tree generator.
For each directory in the directory tree rooted at top (including top
itself, but excluding '.' and '..'), yields a 3-tuple
...
If you want to limit your printed tree to some given depth, probably because you have a lot of nested folders, then you don't want to use os.walk. Instead you want to stop iterating through the nested folders as soon as you've reached your desired depth.
from typing import Union, Optional
from pathlib import Path
def ptree(startpath: Union[str, Path],
max_depth:int = 1,
quick_glance: Optional[int] = None,
_current_depth:int = 0) -> None:
"""
Recursively print directory tree up to a given `max_depth`, specifying if you
like a limited number of files and dirs to include in a `quick_glance`.
Parameters
----------
startpath: Union[str, Path]
The filepath at which to start.
max_depth: int
The maximum depth of nested directories to explore.
quick_glance: Optional[int]
If specified, limits exploration to the first however-many files and dirs.
_current_depth: int
So that we can track our depth as we call the function recursively.
"""
if _current_depth==0:
print(startpath)
else:
print(f'{"--"*_current_depth}{[d for d in startpath.split(os.sep) if d][-1]}')
_current_depth += 1
if _current_depth > max_depth:
return None
else:
ls = os.listdir(startpath)
files = [f for f in ls if os.path.isfile(os.path.join(startpath,f))]
dirs = [d for d in ls if os.path.isdir(os.path.join(startpath,d))]
if quick_glance:
files = files[:quick_glance]
dirs = dirs[:quick_glance]
[print(f'{".."*_current_depth}{f}') for f in files]
[ptree(os.path.join(startpath, d), max_depth, quick_glance, _current_depth)
for d in dirs]
return None

Categories

Resources