I'm doing a Python course on Udacity. And there is a class called Rename Troubles, which presents the following incomplete code:
import os
def rename_files():
file_list = os.listdir("C:\Users\Nick\Desktop\temp")
print (file_list)
for file_name in file_list:
os.rename(file_name, file_name.translate(None, "0123456789"))
rename_files()
As the instructor explains it, this will return an error because Python is not attempting to rename files in the right folder. He then proceeds to check the "current working directory", and then goes on to specify to Python which directory to rename files in.
This makes no sense to me. We are using the for loop to specifically tell Python that we want to rename the contents of file_list, which we have just pointed to the directory we need, in rename_files(). So why does it not attempt to rename in that folder? Why do we still need to figure out cwd and then change it?? The code looks entirely logical without any of that.
Look closely at what os.listdir() gives you. It returns only a list of names, not full paths.
You'll then proceed to os.rename one of those names, which will be interpreted as a relative path, relative to whatever your current working directory is.
Instead of messing with the current working directory, you can os.path.join() the path that you're searching to the front of both arguments to os.rename().
I think your code needs some formatting help.
The basic issue is that os.listdir() returns names relative to the directory specified (in this case an absolute path). But your script can be running from any directory. Manipulate the file names passed to os.rename() to account for this.
Look into relative and absolute paths, listdir returns names relative to the path (in this case absolute path) provided to listdir. os.rename is then given this relative name and unless the app's current working directory (usually the directory you launched the app from) is the same as provided to listdir this will fail.
There are a couple of alternative ways of handling this, changing the current working directory:
os.chdir("C:\Users\Nick\Desktop\temp")
for file_name in os.listdir(os.getcwd()):
os.rename(file_name, file_name.translate(None, "0123456789"))
Or use absolute paths:
directory = "C:\Users\Nick\Desktop\temp"
for file_name in os.listdir(directory):
old_file_path = os.path.join(directory, file_name)
new_file_path = os.path.join(directory, file_name.translate(None, "0123456789"))
os.rename(old_file_path, new_file_path)
You can get a file list from ANY existing directory - i.e.
os.listdir("C:\Users\Nick\Desktop\temp")
or
os.listdir("C:\Users\Nick\Desktop")
or
os.listdir("C:\Users\Nick")
etc.
The instance of the Python interpreter that you're using to run your code is being executed in a directory that is independent of any directory for which you're trying to get information. So, in order to rename the correct file, you need to specify the full path to that file (or the relative path from wherever you're running your Python interpreter).
Related
I've found two ways of listing files from a specified directory from other posts here on Stack Overflow but I can't seem to get them working. The first one returns the path and second return the files I'm looking for but also the path. I have tried several ways like renaming the target directory and files but it doesn't seem to do the trick.
The code in question:
import glob
jpgFilenamesList = glob.glob(r"C:\Users\viodo\PycharmProjects\pythonProject")
print(jpgFilenamesList)
mydir = r"C:\Users\viodo\PycharmProjects\pythonProject"
file_list = glob.glob(mydir + "/*.jpg")
print(file_list)
what I get:
['C:\\Users\\viodo\\PycharmProjects\\pythonProject']
['C:\\Users\\viodo\\PycharmProjects\\pythonProject\\dngjknfjkg.jpg', 'C:\\Users\\viodo\\PycharmProjects\\pythonProject\\fjkdnfkl.jpg', 'C:\\Users\\viodo\\PycharmProjects\\pythonProject\\skdklenfkd.jpg']
Solution found in another thread: Python glob multiple filetypes
Some tweaking got it running smoth. Thanks for the help!
Glob returns a list of pathnames relative to the root directory. That root directory is assumed to be your current working directory unless the glob pattern specified is an absolute path. In short, because your pattern is an absolute path pattern, the returned files will not be relative, but absolute, including the entire path.
When not using an absolute path pattern, in some cases, you could get just a file name if a file name matches in the current working directory. That file name would of course be relative to the current working directory.
In Python 3.10, you should be able to change the assumed root directory without using an absolute pattern via a new root_dir parameter, but this is not currently available in 3.9 and below: https://docs.python.org/3.10/library/glob.html.
In your case, as mentioned in the comments by othes, os.path.basename should be able to get just the file name if that is what you are after. Alternatively, you could change the current working directory via os.chdir and provide a glob pattern of simply *.jpg and get just the file names relative to the that current working directory, both are reasonable solutions.
Extracting the base name:
mydir = r"C:\Users\viodo\PycharmProjects\pythonProject"
file_list = [os.path.basename(f) for f in glob.glob(mydir + "/*.jpg")]
or returning the files relative to an arbitrary "current working directory":
os.chdir(r"C:\Users\viodo\PycharmProjects\pythonProject")
file_list = glob.glob("*.jpg")
Depending on your requirements, one solution may be better than the other.
We are creating a python program that executes specific macros within Polyworks based on user input into the program. Right now the code is:
roto.command.CommandExecute('MACRO EXEC("C:\\RotoWorks\\Macros\\CentrifugalCompressor")')
However this assumes that our program is always installed in C:\RotoWorks. Ideally, our app is portable. I'm sure theres a way to retrieve the filepath that Rotoworks is stored in, then just concatenate the rest of the filepath to the end. How do I do this?
You can retrieve the path from the __file__ attribute of the file. Use os.path.abspath on that attribute to retrieve the absolute path of the file and then os.path.dirname to retrieve the containing directory:
import os
file_directory = os.path.dirname(os.path.abspath(__file__))
path = os.path.join(file_directory, other_path) # join directory to an inner path
roto.command.CommandExecute('MACRO EXEC({})'.format(path))
Use os.path.dirname recursively to move out as many directories as you want.
I'm using Glob.Glob to search a folder, and the sub-folders there in for all the invoices I have. To simplify that I'm going to add the program to the context menu, and have it take the path as the first part of,
import glob
for filename in glob.glob(path + "/**/*.pdf", recursive=True):
print(filename)
I'll have it keep the list and send those files to a Printer, in a later version, but for now just writing the name is a good enough test.
So my question is twofold:
Is there anything fundamentally wrong with the way I'm writing this?
Can anyone point me in the direction of how to actually capture folder path and provide it as path-variable?
You should have a look at this question: Python script on selected file. It shows how to set up a "Sent To" command in the context menu. This command calls a python script an provides the file name sent via sys.argv[1]. I assume that also works for a directory.
I do not have Python3.5 so that I can set the flag recursive=True, so I prefer to provide you a solution which you can run on any Python version (known up to day).
The solution consists in using calling os.walk() to run explore the directories and the set build-in type.
it is better to use set instead of list as with this later one you'll need more code to check if the directory you want to add is not listed already.
So basically you can keep two sets: one for the names of files you want to print and the other one for the directories and their sub folders.
So you can adapat this solution to your class/method:
import os
path = '.' # Any path you want
exten = '.pdf'
directories_list = set()
files_list = set()
# Loop over direcotries
for dirpath, dirnames, files in os.walk(path):
for name in files:
# Check if extension matches
if name.lower().endswith(exten):
files_list.add(name)
directories_list.add(dirpath)
You can then loop over directories_list and files_list to print them out.
I would like to change the cwd to a specific folder.
The folder name is known; however, the path to it will vary.
I am attempting the following but cannot seem to get what I am looking for:
absolute_path = os.path.abspath(folder_name)
directory_path = os.path.dirname(absolute_path)
os.chdir(directory_path)
This does not do what I'm looking for because it is keeping the original cwd to where the .py file is run from. I've tried adding os.chdir(os.path.expanduser("~")) prior to the first code block; however, it just creates the absolute_path to /home/user/folder_name.
Of course if there is a simple import that I could use, I'll be open to anything.
What would be the correct way to get the paths of all folders with with a specific name?
def find_folders(start_path,needle):
for cwd, folders, files,in os.walk(start_path):
if needle in folders:
yield os.path.join(cwd,needle)
for path in find_folders("/","a_folder_named_x"):
print path
all this is doing is walking down your directory structure from a given start path and finding all occurances of a folder named needle
in the example it is starting at the root folder of the system and looking for a folder named "a_folder_named_x" ... be forwarned this could take a while to run if you need to search the whole system ...
You need to understand that abspath accepts a relative pathname (which might just be a filename), and gives you the equivalent absolute (full) pathname. A relative pathname is one that begins in your current directory; no searching is involved, and so it always points to one place (which may or may not exist).
What you actually need is to search down a directory tree, starting at ~ or whatever directory makes sense in your case, until you find a folder with the requested name. That's what #Joran's code does.
I'm trying to get the list of files in a particular directory and count the number of files in the directory. I always get the following error:
WindowsError: [Error 3] The system cannot find the path specified: '/client_side/*.*'
My code is:
print len([name for name in os.listdir('/client_side/') if os.path.isfile(name)])
I followed the code example given here.
I am running the Python script on Pyscripter and the directory /client_side/ do exists. My python code is in the root folder and has a sub-folder called "client_side". Can someone help me out on this?
This error occurs when you use os.listdir on a path which does not refer to an existing path.
For example:
>>> os.listdir('Some directory does not exist')
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
WindowsError: [Error 3] : 'Some directory does not exist/*.*'
If you want to use os.listdir, you need to either guarantee the existence of the path that you would use, or use os.path.exists to check the existence first.
if os.path.exists('/client_side/'):
do something
else:
do something
Suppose your current working directory is c:\foobar, os.listdir('/client_side/') is equivalent to os.listdir('c:/client_side'), while os.listdir('client_side/') is equivalent to os.listdir('c:/foobar/client_side'). If your client_side directory is not in the root, such error will occur when using os.listdir.
For your 0 ouput problem, let us recall os.listdir(path)
Return a list containing the names of the entries in the directory given by path. The list is in arbitrary order. It does not include the special entries '.' and '..' even if they are present in the directory.
and os.path.isfile(path).
Return True if path is an existing regular file. This follows symbolic links, so both islink() and isfile() can be true for the same path.
listdir returns neither the absolute paths nor relative paths, but a list of the name of your files, while isfile requires path. Therefore, all of those names would yield False.
To obtain the path, we can either use os.path.join , concat two strings directly.
print ([name for name in os.listdir(path)
if os.path.isfile(os.path.join(path, name))])
Or
print ([name for name in os.listdir('client_side/')
if os.path.isfile('client_side/' + name)])
I decided to change the code into:
def numOfFiles(path):
return len(next(os.walk(path))[2])
and use the following the call the code:
print numOfFiles("client_side")
Many thanks to everyone who told me how to pass the windows directory correctly in Python and to nrao91 in here for providing the function code.
EDIT: Thank you eryksun for correcting my code!
Two things:
os.listdir() does not do a glob pattern matching, use the glob module for that
probably you do not have a directory called '/client_side/*.*', but maybe one
without the . in the name
The syntax you used works fine, if the directory you look for exists, but there is no directory called '/client_side/.'.
In addition, be careful if using Python 2.x and os.listdir, as the results on windows are different when you use u'/client_side/' and just '/client_side'.
You can do just
os.listdir('client_side')
without slashes.
As I can see a WindowsError, Just wondering if this has something to do with the '/' in windows ! Ideally, on windows, you should have something like os.path.join('C:','client_side')
You want:
print len([name for name in os.listdir('./client_side/') if os.path.isfile(name)])
with a "." before "/client_side/".
The dot means the current path where you are working (i.e. from where you are calling your code), so "./client_side/" represents the path you want, which is specified relatively to your current directory.
If you write only "/client_side/", in unix, the program would look for a folder in the root of the system, instead of the folder that you want.
If you just want to see all the files in the directory where your script is located, you can use os.path.dirname(sys.argv[0]). This will give the path of the directory where your script is.
Then, with fnmatch function you can obtain the list of files in that directory with a name and/or extension specified in the filenamevariable.
import os,sys
from fnmatch import fnmatch
directory = os.path.dirname(sys.argv[0]) #this determines the directory
file_name= "*" #if you want the python files only, change "*" to "*.py"
for path, subdirs, files in os.walk(directory):
for name in files:
if fnmatch(name, file_name):
print (os.path.join(path, name))
I hope this helps.
Checking for existence is subject to a race. Better to handle the error (beg forgiveness instead of ask permission). Plus, in Python 3 you can suppress errors. Use suppress from contextlib:
with suppress(FileNotFoundError):
for name in os.listdir('foo'):
print(name)