Python script to delete old SVN files lacks permission - python

I'm trying to delete old SVN files from directory tree. shutil.rmtree and os.unlink raise WindowsErrors, because the script doesn't have permissions to delete them. How can I get around that?
Here is the script:
# Delete all files of a certain type from a direcotry
import os
import shutil
dir = "c:\\"
verbosity = 0;
def printCleanMsg(dir_path):
if verbosity:
print "Cleaning %s\n" % dir_path
def cleandir(dir_path):
printCleanMsg(dir_path)
toDelete = []
dirwalk = os.walk(dir_path)
for root, dirs, files in dirwalk:
printCleanMsg(root)
toDelete.extend([root + os.sep + dir for dir in dirs if '.svn' == dir])
toDelete.extend([root + os.sep + file for file in files if 'svn' in file])
print "Items to be deleted:"
for candidate in toDelete:
print candidate
print "Delete all %d items? [y|n]" % len(toDelete)
choice = raw_input()
if choice == 'y':
deleted = 0
for filedir in toDelete:
if os.path.exists(filedir): # could have been deleted already by rmtree
try:
if os.path.isdir(filedir):
shutil.rmtree(filedir)
else:
os.unlink(filedir)
deleted += 1
except WindowsError:
print "WindowsError: Couldn't delete '%s'" % filedir
print "\nDeleted %d/%d files." % (deleted, len(toDelete))
exit()
if __name__ == "__main__":
cleandir(dir)
Not a single file is able to be deleted. What am I doing wrong?

To remove recursively all .svn I use this script. May be it will help someone.
import os, shutil, stat
def del_evenReadonly(action, name, exc):
os.chmod(name, stat.S_IWRITE)
os.remove(name)
for root, subFolders, files in os.walk(os.getcwd()):
if '.svn' in subFolders:
shutil.rmtree(root+'\.svn',onerror=del_evenReadonly)

Subversion usually makes all the .svn directories (and everything in them) write protected. Probably you have to remove the write protection before you can remove the files.
I'm not really sure how to do this best with Windows, but you should be able to use os.chmod() with the stat.S_IWRITE flag. Probably you have to iterate through all the files in the .svn directories and make them all writable individually.

Related

How do I delete all folders within a folder except for one in python?

I am modifying a wizard in Kodi and I would like the wizard to delete all folders contained within the "addons" directory without deleting my wizard.
The directory of the folder will be using the "special://" function built into Kodi. I would like to delete everything inside of "special://home/addons" except for the folder named "plugin.video.spartan0.12.0"
I know that python needs to use "xbmc.translatePath" function to recognize the folder path, but I don't know how to delete everything in the folder without deleting "plugin.video.spartan0.12.0"
Any help would be appreciated.
Here is what I currently have
import os
dirPath = "C:\Users\Authorized User\AppData\Roaming\Kodi\addons"
fileList = os.listdir(dirPath)
for fileName in fileList:
os.remove(dirPath+"/"+fileName)
This is probably overkill (and a little sloppy), but it worked for me:
import os
import shutil
#----------------------------------------------------------------------
def remove(path):
"""
Remove the file or directory
"""
if os.path.isdir(path):
try:
shutil.rmtree(path)
except OSError:
print "Unable to remove folder: %s" % path
else:
try:
if os.path.exists(path):
os.remove(path)
except OSError:
print "Unable to remove file: %s" % path
#----------------------------------------------------------------------
def cleanup(dirpath, folder_to_exclude):
for root, dirs, files in os.walk(dirpath, topdown=True):
for file_ in files:
full_path = os.path.join(root, file_)
if folder_to_exclude not in full_path:
print 'removing -> ' + full_path
remove(full_path)
for folder in dirs:
full_path = os.path.join(root, folder)
if folder_to_exclude not in full_path:
remove(full_path)
if __name__ == '__main__':
cleanup(r'c\path\to\addons', 'plugin.video.spartan0.12.0')

If a path is file or directory (Python)

I am trying to list all the files in the current folder and also files in the folders of the current folder.
This is what I have been upto:
import os
def sendFnF(dirList):
for file in dirList:
if os.path.isdir(file):
print 'Going in dir:',file
dirList1= os.listdir('./'+file)
# print 'files in list', dirList1
sendFnF(dirList1)
print 'backToPrevDirectory:'
else:
print 'file name is',file
filename= raw_input()
dirList= os.listdir('./'+filename)
sendFnF(dirList)
This code does get me into folders of the current directory. But when it comes to sub-folders; it treats them as files.
Any idea what I am doing wrong?
Thanks in advance,
Sarge.
Prepending ./ to a path does essentially nothing. Also, just because you call a function recursively with a directory path doesn't change the current directory, and thus the meaning of . in a file path.
Your basic approach is right, to go down a directory use os.path.join(). It'd be best to restructure your code so you listdir() at the start of sendFnF():
def sendFnF(directory):
for fname in os.listdir(directory):
# Add the current directory to the filename
fpath = os.path.join(directory, fname)
# You need to check the full path, not just the filename
if os.path.isdir(fpath):
sendFnF(fpath)
else:
# ...
# ...
sendFnf(filename)
That said, unless this is an exercise, you can just use os.walk()

Moving files and creating directories if certain file type in python

This is probably a simple question, but I'm brand new to python and programming in general.
I'm working on a simple program to copy/move .mp3 files from on location to another while mirroring the directory structure of the source location. What I have so far works, however it also creates new folders in the destination location even if the source folder contained no mp3 files. I only want to create the new directories if the source contains .mp3s, otherwise it could lead to a bunch of empty folders in the destination.
Here is what I have so far:
import os
import shutil #Used for copying files
##CONFIG
source_dir = "C:\Users\username\Desktop\iTunes\\" #set the root folder that you want to scan and move files from. This script will scan recursively.
destPath = "C:\Users\username\Desktop\converted From iTunes" #set the destination root that you want to move files to. Any non-existing sub directories will be created.
ext = ".mp3" #set the type of file you want to search for.
count = 0 #initialize counter variable to count number of files moved
##
##FIND FILES
for dirName, subdirList, fileList in os.walk(source_dir):
#set the path for the destination folder(s)
dest = destPath + dirName.replace(source_dir, '\\')
#if the source directory doesn't exist in the destination folder
#then create a new folder
if not os.path.isdir(dest):
os.mkdir(dest)
print('Directory created at: ' + dest)
for fname in fileList:
if fname.endswith(ext) :
#determine source & new file locations
oldLoc = dirName + '\\' + fname
newLoc = dest + '\\' + fname
if os.path.isfile(newLoc): # check to see if the file already exists. If it does print out a message saying so.
print ('file "' + newLoc + fname + '" already exists')
if not os.path.isfile(newLoc): #if the file doesnt exist then copy it and print out confirmation that is was copied/moved
try:
shutil.move(oldLoc, newLoc)
print('File ' + fname + ' copied.')
count = count + 1
except IOError:
print('There was an error copying the file: "' + fname + '"')
print 'error'
print "\n"
print str(count) + " files were moved."
print "\n"
so if the folder structure is something like:
root->
band 1->
album name->
song.m4a,
song2.m4a
right now it will create all those folders in the destination driectory, even though there are no .mp3s to copy.....
Any help is appreciated!
I think I would create my own wrapper around copy for this sort of thing:
def fcopy(src,dest):
"""
Copy file from source to dest. dest can include an absolute or relative path
If the path doesn't exist, it gets created
"""
dest_dir = os.path.dirname(dest)
try:
os.makedirs(dest_dir)
except os.error as e:
pass #Assume it exists. This could fail if you don't have permissions, etc...
shutil.copy(src,dest)
Now you can just walk the tree calling this function on any .mp3 file.
The simplest thing to do I can think of for your existing code would be to just make it skip over any folders that don't have any .mp3 files in them. This can easily be done by adding the following items and if statement to the top of your loop. The itertools.ifilter() and fnmatch.fnmatch() functions can be used together to simplify checking for files with the proper extension.
from itertools import ifilter
from fnmatch import fnmatch
ext = '.mp3'
fnPattern = '*'+ext
for dirName, subdirList, fileList in os.walk(source_dir):
if not any(ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)):
print ' skipping "{}"'.format(dirName)
continue
...
You will also have to change the os.mkdir(dest) to os.makedirs(dest) in the code further down to ensure that any subdirectories skipped by earlier iterations get created when there's a need to copy files to a corresponding subbranch of the destination directory.
You could optimize things a bit by creating and saving a possibly empty iterator of matching files that have the extension, and then use it again later to to determine what files to copy:
from itertools import ifilter
from fnmatch import fnmatch
ext = '.mp3'
fnPattern = '*'+ext
for dirName, subdirList, fileList in os.walk(source_dir):
# generate list of files in directory with desired extension
matches = ifilter(lambda fname: fnmatch(fname, fnPattern), fileList)
# skip subdirectory if it does not contain any files of interest
if not matches:
continue
...
... create destination directory with os.makedirs()
...
# copy each file to destination directory
for fname in matches:
... copy file
Would shutils.copytree not do what you want in fewer lines?

shutil moving files keeping the same directory structure

I want to move a lot of files. the path to these files is stored in a list. I want to keep the whole directory structure but want to move them to a different folder.
So for example the files are
D:\test\test1\test1.txt
D:\test\test1\test2.txt
I want to move them to C:\ from D:\ and keep the directory structure. How should I go about doing it?
this is the code i have, it is not working
import os, fnmatch
import shutil
f=open('test_logs.txt','r') #logs where filenames are stored with filenames as first entry
for line in f:
filename=line.split()
output_file="C:" + filename[0].lstrip("D:")
shutil.move(filename[0],output_file)
I read the file name fine and I can generate the destination filename fine but when I run it, it gives me an error saying "No such file or directory" (and gives the path of the output filename).
I think you want something like this:
import sys
import os
import shutil
# terminology:
# path = full path to a file, i.e. directory + file name
# directory = directory, possibly starting with a drive
# file name = the last component of the path
sourcedrive = 'D:'
destdrive = 'C:'
log_list_file = open('test_logs.txt', 'r')
for line in log_list_file:
sourcepath = line.split()[0] # XXX is this correct?
if sourcepath.startswith(sourcedrive):
destpath = sourcepath.replace(sourcedrive, destdrive, 1)
else:
print >>sys.stderr, 'Skipping %s: Not on %s' % (sourcepath, sourcedrive)
continue
destdir = os.path.dirname(destpath)
if not os.path.isdir(destdir):
try:
os.makedirs(destdir)
except (OSError, IOError, Error) as e:
print >>sys.stderr, 'Error making %s: %s' % (destdir, e)
continue
try:
shutil.move(sourcepath, destpath)
except (OSError, IOError, Error) as e:
print >>sys.stderr, 'Error moving %s to %s: %s' % (sourcepath, destpath, e)
Do you also want to remove the source directory if it's empty?
Update: Ah, ok, I see the problem -- shutil.move won't copy to a nonexistent directory. To do what you're trying to do, you have to create the new directory tree first. Since it's a bit safer to use a built-in move function than to roll your own copy-and-delete procedure, you could do this:
with open('test_logs.txt','r') as f:
files_to_copy = [line.split()[0] for line in f]
paths_to_copy = set(os.path.split(filename)[0] for filename in files_to_copy)
def ignore_files(path, names, ptc=paths_to_copy):
return [name for name in names if os.path.join(path, name) not in ptc]
shutil.copytree(src, dst, ignore=ignore_files)
for filename in files_to_copy:
output_file="C:" + filename.lstrip("D:")
shutil.move(filename, output_file)
Let me know if that doesn't work
Original Post: If you want to move only some of the files, your best bet is to use shutil.copytree's ignore keyword. Assuming your list of files includes full paths and directories (i.e. ['D:\test\test1\test1.txt', 'D:\test\test1\test2.txt', 'D:\test\test1']), create an ignore_files function and use it like this:
files_to_copy = ['D:\test\test1\test1.txt', 'D:\test\test1\test2.txt', 'D:\test\test1']
def ignore_files(path, names, ftc=files_to_copy):
return [name for name in names if os.path.join(path, name) not in ftc]
shutil.copytree(src, dst, ignore=ignore_files)
Then you can just delete the files in files_to_copy:
for f in files_to_copy:
try:
os.remove(f)
except OSError: # can't remove() a directory, so pass
pass
I tested this -- make certain that you include the paths you want to copy as well as the files in files_to_copy -- otherwise, this will delete files without copying them.

Deleting files which start with a name Python

I have a few files I want to delete, they have the same name at the start but have different version numbers. Does anyone know how to delete files using the start of their name?
Eg.
version_1.1
version_1.2
Is there a way of delting any file that starts with the name version?
Thanks
import os, glob
for filename in glob.glob("mypath/version*"):
os.remove(filename)
Substitute the correct path (or . (= current directory)) for mypath. And make sure you don't get the path wrong :)
This will raise an Exception if a file is currently in use.
If you really want to use Python, you can just use a combination of os.listdir(), which returns a listing of all the files in a certain directory, and os.remove().
I.e.:
my_dir = # enter the dir name
for fname in os.listdir(my_dir):
if fname.startswith("version"):
os.remove(os.path.join(my_dir, fname))
However, as other answers pointed out, you really don't have to use Python for this, the shell probably natively supports such an operation.
In which language?
In bash (Linux / Unix) you could use:
rm version*
or in batch (Windows / DOS) you could use:
del version*
If you want to write something to do this in Python it would be fairly easy - just look at the documentation for regular expressions.
edit:
just for reference, this is how to do it in Perl:
opendir (folder, "./") || die ("Cannot open directory!");
#files = readdir (folder);
closedir (folder);
unlink foreach (grep /^version/, #files);
import os
os.chdir("/home/path")
for file in os.listdir("."):
if os.path.isfile(file) and file.startswith("version"):
try:
os.remove(file)
except Exception,e:
print e
The following function will remove all files and folders in a directory which start with a common string:
import os
import shutil
def cleanse_folder(directory, prefix):
for item in os.listdir(directory):
path = os.path.join(directory, item)
if item.startswith(prefix):
if os.path.isfile(path):
os.remove(path)
elif os.path.isdir(os.path.join(directory, item)):
shutil.rmtree(path)
else:
print("A simlink or something called {} was not deleted.".format(item))
import os
import re
directory = "./uploaded"
pattern = "1638813371180"
files_in_directory = os.listdir(directory)
filtered_files = [file for file in files_in_directory if ( re.search(pattern,file))]
for file in filtered_files:
path_to_file = os.path.join(directory, file)
os.remove(path_to_file)

Categories