Python Forum

Full Version: Create new folders and copy files
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
My task: I have a folder containing many folders named after projects. These project folders have various amounts/levels of subfolders. I want to create new folders with the same name as the project folders in another folder. Then I want to copy files with specific names into the correct newly created project folder. After hours and hours of trial and error I just used chat gpt and got what I belive is pretty close but with one problem that I cant solve. The code creates new folders for all subfolder and not just the project folder. I just want 1 level, new project folders and then all files containing the search words.
(I never coded in Python before and im pretty shit at coding overall so I hope I explained my problem sufficiently)

my(chat gpts) code:
import os
import shutil

# Define the search keywords
search_words = ['Genomförandebeskrivning', 'administrativa föreskrifter', 'AF', 'GB', 'mervärdersbeskrivning']

# Define the input and output directories
input_dir = r'C:\Users\erict\OneDrive\Skrivbord\Anbud'
output_dir = r'C:\Users\erict\OneDrive\Skrivbord\Kopior'

# Walk through the directories and files in the input directory
for dirpath, dirnames, filenames in os.walk(input_dir):
    # Get the corresponding subdirectory path in the output directory
    rel_dirpath = os.path.relpath(dirpath, input_dir)
    output_subdir = os.path.join(output_dir, rel_dirpath)
    os.makedirs(output_subdir, exist_ok=True)

    # Loop through the files in the current directory
    for filename in filenames:
        # Check if the file contains any of the search keywords
        if any(word in filename for word in search_words):
            # Copy the file to the output directory
            input_filepath = os.path.join(dirpath, filename)
            output_filepath = os.path.join(output_subdir, filename)
            shutil.copy2(input_filepath, output_filepath)
(Mar-21-2023, 10:29 PM)cocobolli Wrote: [ -> ]I just want 1 level, new project folders and then all files containing the search words.
I can give you a function to traverse all the files of a single directory, recursively or not recursively. This should give you a simple starting point.
import os

def iter_files(directory, recursive=False):
    walk = os.walk(directory)
    if recursive:
        for root, dirs, files in walk:
            for f in files:
                yield os.path.join(root, f)
    else:
        yield from next(walk)[2]


if __name__ == '__main__':
    for f in iter_files('my-directory', recursive=True):
        print(f)
Instead of walk, you could use glob with pathlib.


import shutil
from pathlib import Path

# if those files do have an extension,
# then you can use path.stem, to get only the name without extension of a Path object
search_words = [
    "Genomförandebeskrivning",
    "administrativa föreskrifter",
    "AF",
    "GB",
    "mervärdersbeskrivning",
]

# more dynamic
# Home is known to the os, then OneDrive/Skrivbord added to path
# then input_dir and output_dir is constructed with the true-division operator
# you can use forward slashes for Windows

base_path = Path.home().joinpath("OneDrive/Skrivbord")
input_dir = base_path / "Anbud"
output_dir = base_path / "Kopior"

for source_element in input_dir.rglob("*"):
    if source_element.is_file() and source_element.stem in search_words:
        target_element = output_dir / source_element.relative_to(input_dir)
        print(source_element, "->", target_element)

        # you culd skip already existing files in the target
        if target_element.exists():
            print(target_element, "exists already")
            print("Skipping ...")
            # you could also check, if the file has the same metadata and size
            # which is not a guarantee that there is no difference in source/target
            # because the metadata and size can be the same, but content can be different
            continue

        # creating the target-directory with all it's parents
        # and ignoring a existing path
        target_element.parent.mkdir(exist_ok=True, parents=True)

        # do not try to blindly copy files, before you have
        # checked if the output is right
        shutil.copy2(source_element, target_element)
EDIT: I've forgotten to add the step where the target-directory is created before the file is copied to the target.
(Mar-22-2023, 10:06 AM)DeaD_EyE Wrote: [ -> ]you could use glob with pathlib.
The drawback of this is that you need to use .is_file() for every path found, so a stat system call for each path. One may expect that os.walk is more efficiently implemented (although I don't have a proof of this).