I need to copy a large folder and rename all the files and folders inside if they contain a specific string. Basically I want to copy everything and change any instance of 10 to 11.
For example if I have a folder structured like this:
mainfolder10
-group10
-group10.js
-group10.html
I want it to copy it like this:
mainfolder11
-group11
-group11.js
-group11.html
I could also copy it first with cp -r mainfolder10/ mainfolder11/ and then use a different command or script to rename the files. I'm just looking for anything to not have to do this manually.
I am looking to accomplish this in either bash, node, or python...whatever you all recommend. Does anyone know a simple way to do this?
The usual technique for recursion over directories and files is to use os.walk():
for root, dirs, files in os.walk('somepath'):
...
From there, you can use os.rename() or any of the shutils as needed on a file-by-file or directory-by-directory basis.
To avoid confusion, I would rename all the files on the first pass and then make a second pass to rename the directories.
So I finally ended up accomplishing this with the following script.
#!/usr/bin/env python
# coding: utf-8
from pathlib import Path
import shutil
import tempfile
cohorts_master = Path.home() / "/Users/leo/Desktop/repos/drive-scripts"
cohorts_master
NEEDLE, REPLACEMENT = (f"C{i}" for i in range(39, 41))
src = cohorts_master / f"MIA {NEEDLE}"
src
assert src.exists(), f"'{src}' does not exist"
dest = Path(tempfile.mkdtemp()) / f"MIA {REPLACEMENT}"
print(dest)
try: # I get errors here. Likely permissions problems. I was getting a partial copy
shutil.copytree(src, dest)
except Exception as err:
pass
for item in dest.glob("**/*"):
shutil.move(item.as_posix(), item.as_posix().replace(NEEDLE, REPLACEMENT))
new_src = dest
new_dest = cohorts_master / dest.name.replace(
REPLACEMENT, f"{REPLACEMENT}-copied-from-{NEEDLE}"
)
new_src, new_dest
shutil.copytree(new_src, new_dest)
Related
I am trying to rename folders in bulk based on the folders/files contained within them (then moving the image files at the end of each path into they're respective model/color folder directories).
Each folder/file has a similar naming convention of MODEL_COLOR.
The code below works, though seems to only be working correctly on the first folder, in other words, the folders are being renamed correctly but the last leg of code seems to be taking the folder which contains the images and moves it to the corresponding path, instead of specifically moving the images to the corresponding path and dropping the folder they're originally in.
On the first folder the loop iterates, it actually moves the images to the correct Model > Color directory, though on all folders after that it seems to be moving the folder containing the images into the correct Model > Color directory, instead of just moving the images alone into the corresponding directory.
After looking at the forums I've seen similar issues where when changing the directory or deleting certain instances, the loop can't iterate correctly due to the initial set changing during the looping process (i.e. deleting or renaming part of the path while iterating). I'm pretty sure it's a simple fix but I can't seem to find the solution that'll work best.
Standard FolderNames:
CL4003IN_45F
CL4003IN_56F
CL40157U_01D
CL40157U_52H
import glob, os, shutil
folder = 'C:\\testing'
# create new folder directory based on Model/Color [is working, but moves file_path into base directory]
# arr = []
for file_path in glob.glob(os.path.join(folder, '*_*')):
new_dir = file_path.rpartition('_')[0]
new_subdir = file_path.rpartition('_')[2]
try:
os.mkdir(os.path.join(new_dir, new_subdir))
except WindowsError:
# Handle the case where the target dir already exist.
pass
shutil.move(file_path, os.path.join(new_dir, new_subdir))
# arr.append(file_path)
Completing the iteration of glob before the loop by storing it in a list helped avoid some unwanted errors.
#...
for file_path in list(glob.glob(os.path.join(folder, '*_*')
...#
But by modifying my code and removing the following from the loop:
try:
os.mkdir(os.path.join(new_dir, new_subdir))
except WindowsError:
pass
Allowed the code to iterate through all the folders in the directory without transferring the folder before the file into the new_dir > new_subdir directory.
The new code that works across a multitude of folders within a directory is:
import glob, os, shutil
folder = 'C:\\testing'
# create new folder directory based on Model > Color
for file_path in list(glob.glob(os.path.join(folder, '*_*'), recursive=True)):
new_dir = file_path.rpartition('_')[0]
new_subdir = file_path.rpartition('_')[2]
shutil.move(file_path, os.path.join(new_dir, new_subdir))
This may not be the most efficient code (and may not work across all instances, that remains to be determined!), though definitely works as intended for now.
Special thanks to those that helped with their suggestions.
The reason why you don't see the actual error is that you catch too many errors in your except: statement.
You intend to catch FileExistsError, so you should also just look for this one. Otherwise you would have noticed that the code in fact throws a FileNotFoundError.
The reason for that is that os.mkdir does not automatically create parent directories. It only creates directories one layer deep, but your code requires two layers of new directories.
For that, you would have to use os.makedirs(...) instead. Conveniently, os.makedirs also accepts an exist_ok flag, which gets rid of your entire try:-except: construct.
A further annotation is that you have a lot of duplicate calculations in your code:
file_path.rpartition('_') gets calculated twice
os.path.join(new_dir, new_subdir) gets calculated twice
I'd suggest storing those in meaningful variables. This speeds up your code, makes it more readable and more maintainable.
Here's a reworked version of your code:
import glob
import os
import shutil
folder = 'C:\\testing'
for file_path in glob.glob(os.path.join(folder, '*_*')):
(new_dir, _, new_subdir) = file_path.rpartition('_')
target_path = os.path.join(new_dir, new_subdir)
os.makedirs(target_path, exist_ok=True)
shutil.move(file_path, target_path)
Further improvements/fixes
There's still a bunch wrong in your code:
You don't check if the thing found by glob is a file
Splitting by _ does not stop at the directory divider. This means that something like C:\\my_files\\bla will get split into C:\\my and files\\bla.
I think you didn't care about either of those, because you thought 'the user would not use the script like this'. But this is actually a case that will happen:
You have a file C:\\my_files\\CL4003IN_45F, which will get moved to C:\\my_files\\CL4003IN\\45F\\CL4003IN_45F, as expected.
You run the script again. The script will find C:\\my_files\\CL4003IN. It doesn't check if it's a folder, so it will process it anyway. It will then split it into C:\\my and files\\CL4003IN.
The entire folder C:\\my_files\\CL4003IN will get moved to C:\\my\\files\\CL4003IN. Therefore the original file CL4003IN_45F ends up in C:\\my\\files\\CL4003IN\\CL4003IN\\45F\\CL4003IN_45F
The solution is:
only use rpartition on the filename, not the entire path
check if it actually is a file, or a directory
Most of these tasks get solved easier with pathlib. I took the liberty of rewriting your code and fixing those issues:
from pathlib import Path
folder = Path('C:\\testing')
for file_path in folder.iterdir():
file_name = file_path.name
# Ignore directories
if not file_path.is_file():
continue
# Split the filename by '_'
(target_dir, _, target_subdir) = file_name.rpartition('_')
# If no '_' found, ignore
if not target_dir:
continue
# Compute the target path and create it if necessary
target_path = folder / target_dir / target_subdir
target_path.mkdir(parents=True, exist_ok=True)
# Move the file to its new position
target_file_path = target_path / file_name
file_path.rename(target_file_path)
One last remark: folder.iterdir() actually does return an iterator. But that shouldn't be a problem in this case, as we explicitely check if the path is an existing file and not a directory or something that already got deleted. But if you want to be 100% safe write list(folder.iterdir()).
What I have is an initial directory with a file inside D:\BBS\file.x and multiple .txt files in the work directory D:\
What I am trying to do is to copy the folder BBS with its content and incrementing it's name by number, then copy/move each existing .txt file to the newly created directory to make it \BBS1, \BBS2, ..., BBSn (depends on number of the txt).
Visual example of the Before and After:
Initial view of the \WorkFolder
Desired view of the \WorkFolder
Right now I have reached only creating of a new directory and moving txt in it but all at once, not as I would like to. Here's my code:
from pathlib import Path
from shutil import copy
import shutil
import os
wkDir = Path.cwd()
src = wkDir.joinpath('BBS')
count = 0
for content in src.iterdir():
addname = src.name.split('_')[0]
out_folder = wkDir.joinpath(f'!{addname}')
out_folder.mkdir(exist_ok=True)
out_path = out_folder.joinpath(content.name)
copy(content, out_path)
files = os.listdir(wkDir)
for f in files:
if f.endswith(".txt"):
shutil.move(f, out_folder)
I kindly request for assistance with incrementing and copying files one by one to the newly created directory for each as mentioned.
Not much skills with python in general. Python3 OS Windows
Thanks in advance
Now, I understand what you want to accomplish. I think you can do it quite easily by only iterating over the text files and for each one you copy the BBS folder. After that you move the file you are currently at. In order to get the folder_num, you may be able to just access the file name's characters at the particular indexes (e.g. f[4:6]) if the name is always of the pattern TextXX.txt. If the prefix "Text" may vary, it is more stable to use regular expressions like in the following sample.
Also, the function shutil.copytree copies a directory with its children.
import re
import shutil
from pathlib import Path
wkDir = Path.cwd()
src = wkDir.joinpath('BBS')
for f in os.listdir(wkDir):
if f.endswith(".txt"):
folder_num = re.findall(r"\d+", f)[0]
target = wkDir.joinpath(f"{src.name}{folder_num}")
# copy BBS
shutil.copytree(src, target)
# move .txt file
shutil.move(f, target)
I have a directory structure that might look something like
Data
Current
A
B
C
Previous
A
X
In as simple/quick a step as possible, I want to rename Current as Previous including the contents and wiping out the original such that it is now:
Data
Previous
A
B
C
I've tried something like:
from pathlib import Path
src = Path('Data/Current')
dest = Path('Data/Previous')
src.replace(dest)
The docs led me to hope this would work:
If target points to an existing file or directory, it will be
unconditionally replaced.
But it does appear to be conditional. I get a Directory not empty exception. I guess I could recursively delete the Previous directory first. Is that basically the only solution? Or is there a better way to achieve this?
(I prefer pathlib, but if os or shutil is the better hammer here, I'm not opposed to them)
(I am running on Linux)
Continuing from sehafoc's comment, after removing the entire file tree for dest ("Data/Previous") using shutil this way:
shutil.rmtree(dest)
Rename src ("Data/Current") to dest .
src.rename(dest)
I'm new to Python and really want this command to work so I have been looking around on google but I still can't find any solution. I'm trying to make a script that deletes a folder inside the folder my Blender game are inside so i have been trying out those commands:
import shutil
from bge import logic
path = bge.logic.expandPath("//")
shutil.rmtree.path+("/killme") # remove dir and all contains
The Folder i want to delete is called "killme" and I know you can just do: shutil.rmtree(Path)
but I want the path to start at the folder that the game is in and not the full C:/programs/blabla/blabla/test/killme path.
Happy if someone could explain.
I think you are using shutil.rmtree command in wrong way. You may use the following.
shutil.rmtree(path+"/killme")
Look at the reference https://docs.python.org/3/library/shutil.html#shutil.rmtree
Syntax: shutil.rmtree(path, ignore_errors=False, onerror=None)
Assuming that your current project directory is 'test'. Then, your code will look like the follwing:
import shutil
from bge import logic
path = os.getcwd() # C:/programs/blabla/blabla/test/
shutil.rmtree(path+"/killme") # remove dir and all contains
NOTE: It will fail if the files are read only in the folder.
Hope it helps!
What you could do is set a base path like
basePath = "/bla_bla/"
and then append the path and use something like:
shutil.rmtree(basePath+yourGamePath)
If you are executing the python as a standalone script that is inside the desired folder, you can do the following:
#!/usr/bin/env_python
import os
cwd = os.getcwd()
shutil.rmtree(cwd)
Hope my answer was helpful
The best thing you could do is use the os library.
Then with the os.path function you can list all the directories and filenames and hence can delete/modify the required folders while extractring the name of folders in the same way you want.
for root, dirnames, files in os.walk("issues"):
for name in dirnames:
for filename in files:
*what you want*
Im rather new to python but I have been attemping to learn the basics.
Anyways I have several files that once i have extracted from their zip files (painfully slow process btw) produce several hundred subdirectories with 2-3 files in each. Now what I want to do is extract all those files ending with 'dem.tif' and place them in a seperate file (move not copy).
I may have attempted to jump into the deep end here but the code i've written runs without error so it must not be finding the files (that do exist!) as it gives me the else statement. Here is the code i've created
import os
src = 'O:\DATA\ASTER GDEM\Original\North America\UTM Zone 14\USA\Extracted' # input
dst = 'O:\DATA\ASTER GDEM\Original\North America\UTM Zone 14\USA\Analyses' # desired location
def move():
for (dirpath, dirs, files) in os.walk(src):
if files.endswith('dem.tif'):
shutil.move(os.path.join(src,files),dst)
print ('Moving ', + files, + ' to ', + dst)
else:
print 'No Such File Exists'
First, welcome to the community, and python! You might want to change your user name, especially if you frequent here. :)
I suggest the following (stolen from Mr. Beazley):
# genfind.py
#
# A function that generates files that match a given filename pattern
import os
import shutil
import fnmatch
def gen_find(filepat,top):
for path, dirlist, filelist in os.walk(top):
for name in fnmatch.filter(filelist,filepat):
yield os.path.join(path,name)
# Example use
if __name__ == '__main__':
src = 'O:\DATA\ASTER GDEM\Original\North America\UTM Zone 14\USA\Extracted' # input
dst = 'O:\DATA\ASTER GDEM\Original\North America\UTM Zone 14\USA\Analyses' # desired location
filesToMove = gen_find("*dem.tif",src)
for name in filesToMove:
shutil.move(name, dst)
I think you've mixed up the way you should be using os.walk().
for dirpath, dirs, files in os.walk(src):
print dirpath
print dirs
print files
for filename in files:
if filename.endswith('dem.tif'):
shutil.move(...)
else:
...
Update: the questioner has clarified below that he / she is actually calling the move function, which was the first point in my answer.
There are a few other things to consider:
You've got the order of elements returned in each tuple from os.walk wrong, I'm afraid - check the documentation for that function.
Assuming you've fixed that, also bear in mind that you need to iterate over files, and you need to os.join each of those to root, rather than src
The above would be obvious, hopefully, if you print out the values returned by os.walk and comment out the rest of the code in that loop.
With code that does potentially destructive operations like moving files, I would always first try some code that just prints out the parameters to shutil.move until you're sure that it's right.
Any particular reason you need to do it in Python? Would a simple shell command not be simpler? If you're on a Unix-like system, or have access to Cygwin on Windows:
find src_dir -name "*dem.tif" -exec mv {} dst_dir