I have a python tool to 'touch' (utime) a file, then move to another folder. However, if the file already exists in the destination folder, it is silently overwritten. I would like to check for a file of the same name in the destination folder and, if it exists, rename the one I am moving to its name plus '-n' at the end, where n is a number starting at '1' and, if the file with '-1' at the end already exists, '-2' etc.
For example, say in the source folder is a file 'foo.txt', but there is one 'foo.txt' in the destination folder as well. This function should return '(absolute path)/foo-1.txt'.
So, I have made a function to check for these circumstances and return the modified string, so I can use rename later and not overwrite. However, currently, if the file exists, it returns nothing. Following is the function code, assuming these vars:
fileName - input filepath, absolute path. e.g. /Users/foo/sourceFolder/bar.txt
index - iterator variable, set to '1' at the start of each file being opened (externally to function)
def checkExists(fileName):
global index
print "checkExists(" + fileName + ")"
if exists(fileName):
splitPath = split(newFile)
splitName = splitext(splitPath[1])
newSplitName = splitName[0] + "-" + str(index)
index += 1
newName = splitPath[0] + "/" + newSplitName + splitName[1]
print "newName = " + newName
else:
print "(else) fileName = " + fileName
print "(else) fileName = " + str(type(fileName))
print ""
return fileName
checkExists(newName)
Now it seems that the inner call for checkExists() at the end is not running.
I hope I have been clear in my explanation.
IAmThePiGuy
P.S. I do not want to hear about potential race problems with utime, I know that the files in the source directory will not be accessed by anything else.
Your problem is that, if the file exists, you don't return anything. I think you're intending to use recursion to check the new filename, but you don't return the result of that call. Here's a stab:
def checkExists(fileName, index=0):
print "checkExists(" + fileName + ")"
if exists(fileName):
splitPath = split(newFile)
splitName = splitext(splitPath[1])
newSplitName = splitName[0] + "-" + str(index)
index += 1
newName = splitPath[0] + "/" + newSplitName + splitName[1]
print "newName = " + newName
return checkExists(newName, index) # recurse
else:
print "(else) fileName = " + fileName
print "(else) fileName = " + str(type(fileName))
print ""
return fileName
I also took the liberty of moving the recursive call closer to the generation of newName and removing the global variable and replacing that with recursion as well.
Here's an iterative approach to a similar problem:
def copyfile(path, dstdir, verbose=True, dryrun=False):
"""Copy `path` file to `dstdir` directory incrementing name if necessary."""
filename = os.path.basename(path)
basename, ext = os.path.splitext(filename)
for i in itertools.count(2):
destpath = os.path.join(dstdir, filename)
if not os.path.exists(destpath):
if verbose:
print(path, '->', destpath)
if not dryrun:
shutil.copyfile(path, destpath)
return
# increment filename
filename = "%s_%02d%s" % (basename, i, ext)
Related
Python novice, my simple script gets a given directory and renames all files sequentially, however it is deleting the files but the print is showing the files names getting renamed, not sure where its going wrong here.
Also, in what order does it retrieve these files?
import os
path = os.path.abspath("D:\Desktop\gp")
i = 0
for file_name in os.listdir(path):
try:
print (file_name + " - " + str(i))
os.rename(os.path.join(path,file_name), str(i))
except WindowsError:
os.remove(str(i))
os.rename(os.path.join(path,file_name), str(i))
i += 1
print(str(i) + " files.")
Edit
Below is the solution with working code, retrieves all files in a dir by creation date and assigns them a iterated number while retaining file extension.
import os
def sorted_dir(folder):
def getctime(name):
path = os.path.join(folder, name)
return os.path.getctime(path)
return sorted(os.listdir(path), key=getctime)
path = os.path.abspath("D:\Path\Here")
i = 0
for file_name in sorted_dir(path):
_, ext = os.path.splitext(file_name)
print (file_name + " - " + str(i)+ext)
os.rename(os.path.join(path,file_name), os.path.join(path, str(i) + ext))
i += 1
print(str(i-1) + " files.")
The problem is that you're using an absolute path for the source, but a relative path for the destination. So the files aren't getting deleted, they're just getting moved into the current working directory.
To fix it so they get renamed into the same directory they were already in, you can do the same thing on the destination you do on the source:
os.rename(os.path.join(path,file_name), os.path.join(path, str(i)))
From a comment, it sounds like you may want to preserve the extensions on these files. To do that:
_, ext = os.path.splitext(file_name)
os.rename(os.path.join(path,file_name), os.path.join(path, str(i) + ext))
I'm trying to rename files in a directory so that it the new name is the original name followed by a space + "17-" + an incrementally increasing number.
The code below is just renaming files from 151, upward. How do I keep the original name, adding the text "17-" and the numbers?
import os
path = 'C:\Users\dcs\Desktop\Test direct'
files = os.listdir(path)
i = 151
for file in files:
os.rename(os.path.join(path, file), os.path.join(path, str(i)+'.TIF'))
i = i+1
Simply by writing that concatenation expression. If I understand your details correctly, your new loop body would be
new_name = file + " 17-" + str(i) + ".TIF"
os.rename(os.path.join(path, file),
os.path.join(path, new_name) )
i += 1
This would change file "ABC" into file "ABC 17-151.TIF"; the next would contain "17-152", and so on.
FACEPALM
file is a built-in type. Change the loop index.
for fname in files:
new_name = fname + " 17-" + str(i) + ".TIF"
os.rename(os.path.join(path, fname), new_name)
i += 1
If I understand Prune's suggestion above, which I obviously don't, it would look like:
import os
path = 'C:\Users\dcs\Desktop\Test direct'
files = os.listdir(path)
i = 151
#The part that I want to strip the extensions from
for file in files:
new_name = file[:-3]
#The part that words correctly (thanks Prune)
for fname in files:
new_name = fname + " 17-" + str(i) + ".TIF"
os.rename(os.path.join(path, fname), new_name)
i += 1
However the first part, meant to strip the file of it's extension isn't working.
Ive successfully made a script which changes all $ to # in a directory, but it doesnt affect the foldername. So the foldername still remains the same name. How can I modify this to change the $ to # in the foldername as well?
def cleanFiles(self):
directoryChosen = self.directoryChoice()
print directoryChosen + " you made it to files selected"
#for file_names in os.listdir(directoryChosen):
#self.listWidget.addItem(file_names)
for n in os.listdir(directoryChosen):
print n + " made it here"
self.listWidget.addItem(n)
if os.path.isdir(directoryChosen):
print directoryChosen + " almost there"
newname = n.replace('$', '#')
print newname + " this is newname"
if newname != n:
print newname
print n
path = os.path.join(directoryChosen + '/' + n)
print path
target = os.path.join(directoryChosen + '/' + newname)
print target
os.rename(path, target)
The first problem you have is that you are doing this:
if os.path.isdir(directoryChosen):
When you want to do this:
if os.path.isdir(n):
So, with that in mind, inside your loop you actually want to reference n, which are the folders and files you are trying to check.
The second problem that you have is in your usage of os.path.join.
You don't have to join the way you are joining. You don't need to add the slash between the two, the join does that for you. I suggest reading the doc on that. So you want this:
path = os.path.join(directoryChosen, n)
target = os.path.join(directoryChosen, newname)
So, the code will then end up looking something like this:
for n in os.listdir(directoryChosen):
print n + " made it here"
self.listWidget.addItem(n)
if os.path.isdir(n):
print directoryChosen + " almost there"
newname = n.replace('$', '#')
print newname + " this is newname"
if newname != n:
print newname
print n
path = os.path.join(directoryChosen, n)
print path
target = os.path.join(directoryChosen, newname)
print target
os.rename(path, target)
if os.path.isdir(directoryChosen):
for n in os.listdir(directoryChosen):
self.listWidget.addItem(n)
newname = n.replace('$', '#')
if newname != n:
path = os.path.join(directoryChosen, n)
target = os.path.join(directoryChosen, newname)
os.rename(path, target)
newdir = directoryChosen.replace('$', '#')
if directoryChosen != newdir
os.rename(directoryChosen, newdir)
I am not sure if I understand your problem correctly. But, you can use the code below:
import os
def cleanFiles():
directoryChosen = "C:\\Test$234"
if os.path.isdir(directoryChosen):
for n in os.listdir(directoryChosen):
newname = n.replace('$', '#')
if newname != n:
path = os.path.join(directoryChosen + '/' + n)
target = os.path.join(directoryChosen + '/' + newname)
os.rename(path, target)
os.rename(directoryChosen, directoryChosen.replace('$','#'))
It renames your chosen directory as well, if that is what you are looking for.
I am trying to change the foldername, and the file names. They all have a $, and I want a #. Here is what I got
def cleanFiles(self):
#directory = '/Users/eeamesX/work/data/Sept_1_upload/priority_2/transcriptsAudoSplits/09012015_331_male3_r1_seg1/IT_007/hell$o '
directoryChosen = self.directoryChoice()
print directoryChosen + " you made it to files selected"
self.listWidget.addItem(directoryChosen)
#for file_names in os.listdir(directoryChosen):
#self.listWidget.addItem(file_names)
for n in os.listdir(directoryChosen):
print n + " made it here"
if os.path.isfile(directoryChosen):
print directoryChosen + "almost there"
newname = n.replace('$', '#')
print newname + " this is newname"
if newname != n:
os.rename(n,newname)
print '\n--------------------------------\n'
for n in os.listdir(directoryChosen):
print n
self.lblNamechange.show()
I have pinpointed my problem through the printing. It is in the line
if os.path.isfile(directoryChosen):
It is not reading the files in the directory to change the filenames. Any help? Also, Will this change the foldername?
If what you are passing is in fact a directory, then this:
if os.path.isfile(directoryChosen):
will never be True, because isfile checks whether what you are passing is in fact a file.
Running help(os.path.isfile) in your interpreter will show you:
isfile(path)
Test whether a path is a regular file
What you want to use is actually isdir:
if os.path.isdir(directoryChosen):
I am working on very large file system. My task is to clean the system with some given parameters. Below program fragment can give a idea.
import DirectoryWalker
extentions_to_delete = list([".rar",".doc",".URL",".js",".EXE",".mht",".css",".txt", ".cache", ".xml"])
extentions_to_copy = list([".jpg",".BMP",".GIF",".jpeg",".gif",".bmp",".png",".JPG"])
dw = DirectoryWalker.DirectoryWalker("/media/08247451247443AA/home/crap/")
def copy_advice(key, files):
for ext in extentions_to_copy:
if(ext == key):
print str(len(files)) + " Files of type " + key + " should be coppied to the target folder."
for file in files:
copy_to = "/media/08247451247443AA/home/crap-pics/"
moved = dw.move_file_to(file, copy_to, True)
if not moved:
print file + " : not moved"
walks = dw.get_all_file_types()
for key in DirectoryWalker.Walk.store.keys():
files = DirectoryWalker.Walk.store[key]
copy_advice(key, files)
In the DirectoryWalker following code is written. Walk is a simple class which have a store object.
def get_all_file_types(self):
extentions = []
for dirpath,dirnames,filenames in os.walk(self.dir_name):
for file in filenames:
extentions.append(Walk(dirpath +"/"+ file))
return extentions
def move_file_to(self, file_path, copy_to, rename_if_exists= False):
file_name = os.path.split(file_path)[1]
target_file_name = copy_to + file_name;
coppied = False
if not os.path.isfile(target_file_name):
coppied = True
try:
os.rename(file_path, target_file_name)
except OSError:
coppied = False
print "Oops! Unable to rename : " + file_path + " to target : " + target_file_name
if rename_if_exists:
coppied = True
file_name = "new_"+ file_name
try:
os.rename(file_path, target_file_name)
except OSError:
coppied = False
print "Oops! Unable to rename : " + file_path + " to target : " + target_file_name
return coppied
The Walk class
class Walk:
store = dict([])
def __init__(self, filename):
self.file_ext = os.path.splitext(filename)[-1]
self.file_name = filename
if not (Walk.store.has_key(self.file_ext)):
Walk.store[self.file_ext] = list()
Walk.store[self.file_ext].append(self.file_name)
But when program executed, it only moves almost 10400 files. But manual calculation suggest, there should be 13400 files in the file system. Please let me know, what I am doing wrong?
Update Solutions
After a careful investigations, I come out with result that there are many ambiguous file names in the target file system and those files were missing.
To answer your question, why not start with a simpler piece of code to test?
import os
all_files = []
for root, dirs, files in os.walk('/media/08247451247443AA/home/crap/'):
all_files.extend(files)
print len(all_files)
As a side note, could you replace the Walk class with a defaultdict?
After a careful investigations, I come out with result that there are many ambiguous file names in the target file system and those files were missing.