Python shutil move I/O error - python

I am trying to find some files, create a folder and move the files in there.
def test():
try:
logfile = "C:\\Users\\alkis\\Desktop\\testouter\\test"
result_dir = os.path.join(logfile, "testzip")
print result_dir
os.makedirs(result_dir)
os.chmod(result_dir, stat.S_IWRITE)
kpath = logfile + "\\*.jpg"
print kpath
files = glob.glob(kpath)
for file in files:
filename = os.path.splitext(file)[0]
print filename
os.chmod(filename, stat.S_IWRITE)
shutil.move(filename, result_dir)
except Exception, e:
#shutil.rmtree(result_dir)
print e.__doc__ + "\r\n"
print e.message
return
The error I am getting is: MS-Windows OS call failed
I check the permissions on my files and they are not read only.

You are listing each file, removing the extension, then trying to move that filename.
The extension is part of the filename, don't remove it. Windows Exlorer hides the extension only when displaying files.
You also don't need to call os.chmod() on the filename; just skip that step:
for file in files:
filename = os.path.splitext(file)[0]
print filename
shutil.move(filename, result_dir)

Related

How to rename multiple files in a folder that follow a specific name pattern

I have multiple files in a folder that are named following this pattern 'AF-A0A0A0MRZ7-F1-model_v4.pdb', 'AF-Q1GBF7-F1-model_v4.pdb', 'AF-X5IWT5-F1-model_v4.pdb' and I would like to rename them for just 'AA0A0A0MRZ7.pdb', 'Q1GBF7.pdb', 'X5IWT5.pdb'.
I tried the script bellow, but it only renamed a few files in the folder.
import os
import shutil
directory = (r'\\...')
for filename in os.listdir(directory):
if filename.startswith('AF-') and filename.endswith('-F1-model_v4.pdb'):
try:
parts = filename.split('-')
new_filename = parts[1]+'.pdb'
old_path = os.path.join(directory, filename)
new_path = os.path.join(directory, new_filename)
shutil.move(old_path, new_path)
except Exception as e:
# Print the error message if an error occurs
print(f'Error renaming file {filename}: {e}')
I think you're almost done, did you already tried the os.rename() function instead of shutil.move()?
os.rename(old_path, new_path)

How to access the last filename in a directory in python

I'm trying to loop through some files in a directory. If the filename has two specific strings together, then I'm supposed to open and read those files for information. However, if none of the files have those two strings, I want to print an error message only once.
for filename in os.listdir(directory):
if filename.find("<string1>") != -1 and filename.find("<string2>") != -1:
#open file
else:
#print error message
I know doing this will print as many error messages as there are files in the directory (i.e. if there's 15 files with no matches, I'll get 15 error messages). But what I want is to only print an error message once after there aren't any matches in any of the N files in directory. I figured I could do something like this:
for filename in os.listdir(directory):
if filename.find("<string1>") != -1 and filename.find("<string2>") != -1:
#open file
else:
if filename[-1]: #if filename is last in directory
#print error message
But I've discovered this doesn't work. How would I get an error message to print only after the last filename has been read and doesn't match?
A simple solution would be to initialize some boolean flag before your for loop, e.g. found = false
If you find a file, set found = true. Then you can check the value of found after your for loop finishes and print the appropriate message based on its value.
Filter the list of files before the for-loop:
filenames = [fname for fname in os.listdir(directory)
if '<string1>' in fname and '<string2>' in fname]
if filenames:
for filename in filenames:
#open file
else:
#print error message
You can probably also use the glob module to get the filenames:
import glob
filenames = glob.glob(directory + '/*string1*string2*')
Another way is to use a variable to check if all the files have been processed. Checked and found it working in Python 2.7
import os
directory = "E:\\test\\"
files_count = len(os.listdir(directory))
files_processed = 0
for filename in os.listdir(directory):
if 'string1' in filename and 'string2' in filename:
#open file
print ("Opening file")
else:
files_processed = files_processed + 1
if (files_processed >= files_count):
print ("error message")
Not sure if this is extreme. But I'd make it a function and raise IOError.
Plus, i'd always use absolute path. Try the pathlib module too
import os
def get_files(directory):
for filename in os.listdir(directory):
if "string1" in filename and "string2" in filename:
yield filename
raise IOError("No such file")
for file in get_files('.'):
print(file)
# do stuff with file

Data Path for File Input Processing

I wanted to supply python with a windows 'data path' that could be used to set up input processing. I googled this with no luck, and now figure I am on my own.
There appears to be many ways of reading in a file with python, and after some frustration with "\" and "/" and windows path names I found a way to get my data path set up. It is not a general approach but should serve me well.
Related Questions: Is this code ugly? Is this a nonstandard method? Are there elegant features in 3.6 that should be used?
### Title: Process an input file using a 'data path' for a user on windows
import sys
import os
print("OK, starting program...")
file_processed = False
for path, dirs, files in os.walk("/Users/Mike"):
if file_processed: break
for file in files:
if file_processed: break
if file == 'seriousdata.txt':
my_path = os.path.abspath(path)
my_dsn = os.path.join(my_path, file)
print("Opening txt file " + my_dsn + " for input.")
with open(my_dsn) as da_lines:
textlines = (line.rstrip('\r\n') for line in da_lines)
for line in textlines:
print(line)
file_processed = True
if file_processed:
pass
else:
print("File not found")
print("OK, program execution has ended.")
sys.exit() # END SAMPLE CODE SNIPPET
From looking at your code, I'm assuming that you want to start at one directory, and move through each child directory, printing out the matching filename's contents if it is found.
If so, then this is way to do this with recursion:
import os
def recursive_list(path, filename):
files = os.listdir(path)
for name in files:
try:
p = os.path.join(path, name)
if os.path.isdir(p):
recursive_list(p, filename)
else:
if name == filename:
with open(p, "r") as f:
print(f.read())
except PermissionError:
pass
return
recursive_list("/home/jebby/Desktop","some_file.txt")
This will start out listing files in path. For every file that is found, if that file is a directory, then the function itself is called (Starting at the path to that folder). If filename matches the name of any file in the current directory, it will be printed (if the user has permissions for that file).
Otherwise, if you only want to read the filename from a known directory without walking down the directory tree:
import os
data_path = "/home/jebby/Desktop"
file_you_want = "filename.txt"
with open(os.path.join(data_path, file_you_want), "r") as f:
content = f.read()
print(content)
The main question would be : Do you know the location of the file?
Jebby has an answer to crawl through the directories.
Here is a solution without using "import os"
dir_fullpath = "c:/project_folder/data"
dir_path = "data"
filename = "file.txt"
try:
f = open(dir_path + "/" + filename, 'r')
# print("open " +dir_path + "\" + filename)
# data=[]
for line in f:
print (line.rstrip())
# data.append(line.rstrip())
f.close()
except IOError:
print("Fail to open" + dir_path + "\" + filename)

"Permission denied" error from downloading all files from FTP folder

So far I have the gotten the names of the files I need from the FTP site. See code below.
from ftplib import FTP
import os, sys, os.path
def handleDownload(block):
file.write(block)
ddir='U:/Test Folder'
os.chdir(ddir)
ftp = FTP('sidads.colorado.edu')
ftp.login()
print ('Logging in.')
directory = '/pub/DATASETS/NOAA/G02158/unmasked/2012/04_Apr/'
print ('Changing to ' + directory)
ftp.cwd(directory)
ftp.retrlines('LIST')
print ('Accessing files')
filenames = ftp.nlst() # get filenames within the directory
print (filenames)
Where I am running into trouble is the download of the files into a folder. The code below is something I have tried however I receive the permission error due to the file not being created before I write to it.
for filename in filenames:
local_filename = os.path.join('C:/ArcGis/New folder', filename)
file = open(local_filename, 'wb')
ftp.retrbinary('RETR '+ filename, file.write)
file.close()
ftp.quit()
Here is the error and callback.
The directory listing includes the . reference to the folder (and probably also .. reference to the parent folder).
You have to skip it, you cannot download it (them).
for filename in filenames:
if (filename != '.') and (filename != '..'):
local_filename = os.path.join('C:/ArcGis/New folder', filename)
file = open(local_filename, 'wb')
ftp.retrbinary('RETR '+ filename, file.write)
file.close()
Actually you have to skip all folders in the listing.

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.

Categories