I have the name of a file and I want to find it on whatever Drive it is. I can use a recursive search, but I need a root directory to start with. For most computers, the root is "C:\". But there are some that have changed the letter, or some that have more than one (eg. C,D...).
So, I need to find a way to know all the Drives in a computer, so I can search through each one. I need to find the letters programmatically without user input.
I know I can use "diskpart", but it requires an administrator access, something my code will not have. There is also "wmic", but I don't know if the computer has the right folder in the PATH.
So, the question is how can I do a recursive search in a computer which I don't know the letters of the drives or how many are there? Or is there another way to find the path of a file in a computer?
I have a solution that requires you run as an administrator, but does not require you install any other tools. I then have an alternative, messy solution that you can run even without any of these privileges. I'll start with the cleaner one.
This script will find you the drives:
import subprocess
driveStr = subprocess.check_output("fsutil fsinfo drives")
driveStr = driveStr.strip().lstrip('Drives: ')
drives = driveStr.split()
Basically, fsutil fsinfo drives is a commandline command that will return the letters of existing drives on your computer. So you can get that result with check_output and then strip away the unnecessary characters, because it will return a string like '\r\nDrives: C:\\ D:\\ \r\n'. Then you can split the list and you'll have a list of drives like this:
['C:\\', 'D:\\']
You can then loop over those drives to recursively search for a file.
import os
def find_file(target, folder):
for f in os.listdir(folder):
path = os.path.join(folder, f)
if os.path.isdir(path):
result = find_file(target, path)
if result is not None:
return result
continue
if f == target:
return path
This function will basically loop over every file in the folder it's provided, it first checks if the file is a folder with isdir, and runs recursively on any folders it finds. If it's not a folder, it will check for the file named target and return its path when found. You can combine these two parts pretty simply:
for drive in drives:
filepath = find_file(target, drive)
if filepath is not None:
break
However, if you're unable to run as administrator, there is another way that's not as nice. Basically you could just loop over all the possible drive names and check if they exist, like this:
drives = ['{}:\\' for letter in 'CDEFGHIJKLMNOPQRSTUVWXYZ']
for drive in drives:
if os.path.isdir(drive):
filepath = find_file(target, drive)
if filepath is not None:
break
This will check if each drive exists as a directory and search each one that does. Note this will include networked paths and I believe USBs/external hard drives, while the alternative method does not.
If I'm understanding correctly you know the name of the file.
Using os.path.abspath('myfile.txt') should return the full path.
Afterwards you can use the os.path.dirname to get only the directory.
According to documentation os.path should be cross-platform: https://docs.python.org/3/library/os.path.html#module-os.path but I haven't tried it on Windows.
Related
lately I started working with the Os module in python . And I finally arrived to this Os.path method . So here is my question . I ran this method in one of my kivy project just for testing and it actually didn't returned the correct output.The method consisted of finding if any directory exist and return a list of folders in the directory . otherwise print Invalid Path and return -1 . I passed in an existing directory and it returned -1 but the weird path is that when I run similar program out of my kivy project using the same path present in thesame folder as my python file it return the desired output .here is the image with the python file and the directory name image I have tested which returns invalid path.
and here is my code snippet
def get_imgs(self, img_path):
if not os.path.exists(img_path):
print("Invalid Path...")
return -1
else:
all_files = os.listdir(img_path)
imgs = []
for f in all_files:
if (
f.endswith(".png")
or f.endswith(".PNG")
or f.endswith(".jpg")
or f.endswith(".JPG")
or f.endswith(".jpeg")
or f.endswith(".JPEG")
):
imgs.append("/".join([img_path, f]))
return imgs
It's tough to tell without seeing the code with your function call. Whatever argument you're passing must not be a valid path. I use the os module regularly and have slowly learned a lot of useful methods. I always print out paths that I'm reading or where I'm writing before doing it in case anything unexpected happens, I can see that img_path variable, for example. Copy and paste the path in file explorer up to the directory and make sure that's all good.
Some other useful os.path methods you will find useful, based on your code:
os.join(<directory>, <file_name.ext>) is much more intuitive than imgs.append("/".join([img_path, f]))
os.getcwd() gets your working directory (which I print at the start of scripts in dev to quickly address issues before debugging). I typically use full paths to play it safe because Python pathing can cause differences/issues when running from cmd vs. PyCharm
os.path.basename(f) gives you the file, while os.path.dirname(f) gives you the directory.
It seems like a better approach to this is to use pathlib and glob. You can iterate over directories and use wild cards.
Look at these:
iterating over directories: How can I iterate over files in a given directory?
different file types: Python glob multiple filetypes
Then you don't even need to check whether os.path.exists(img_path) because this will read the files directly from your file system. There's also more wild cards in the glob library such as * for anything/any length, ? for any character, [0-9] for any number, found here: https://docs.python.org/3/library/glob.html
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 am using os.walk to run through a tree of directories check for some input files and then run a program if the proper inputs are there. I notice I am having a problem because of the away that os.walk is evaluating the root variable in the loop:
for root, dirs, files in os.walk('.'):# I use '.' because I want the walk to
# start where I run the script. And it
# will/can change
if "input.file" in files:
infile = os.path.join(root,"input.file")
subprocess.check_output("myprog input.file", Shell=True)
# if an input file is found, store the path to it
# and run the program
This is giving me an issue because the infile string looks like this
./path/to/input.file
When it needs to look like this for the program to be able to find it
/home/start/of/walk/path/to/input.file
I want to know if there is a better method/ a different way to use os.walk such that I can leave the starting directory arbitrary, but still be able to use the full path to any files that it finds for me. Thanks
The program I am using is written by me in c++ and I suppose I could modify it as well. But I am not asking about how to do that in this question just to clarify this question is about python's os.walk and related topics that is why there is no examples of my c++ code here.
Instead of using ., convert it to the absolute path by using os.path.abspath("."). That will convert your current path to an absolute path before you begin.
I'm doing a program in which Chimera needs to be opened, I'm opening it with:
def generate_files_bat(filename):
f = open(filename, 'w')
text = """echo off SET PATH=%PATH%;"C:\\Program Files (x86)\\Chimera 1.6.1\\bin" chimera colpeps.cmd"""
print >>f, text
f.close()
But I need to find Chimera apart from the computer the python program is running. Is there any way the path can be searched by the python program in any computer?
Generally speaking, I don't think it is such a good idea to search the path for a program. Imagine, for example that two different versions were installed on the machine. Are-you sure to find the right one? Maybe a configuraition file parsed with the standard module ConfigParser would be a better option?
Anyway, to go back to your question, in order to find a file or directory, you could try to use os.walk which recursively walks trough a directory tree.
Here is an example invoking os.walk from a generator, allowing you to collect either the first or all matching file names. Please note that the generator result is only based on file name. If you require more advanced filtering (say, to only keep executable files), you will probably use something like os.stat() to extend the test.
import os
def fileInPath(name, root):
for base, dirs, files in os.walk(root):
if name in files:
yield os.path.join(base, name)
print("Search for only one result:")
print(next(fileInPath("python", "/home/sylvain")))
print("Display all matching files:")
print([i for i in fileInPath("python", "/home/sylvain")])
There is which for Linux and where for Windows. They both give you the path to the executable, provided it lies in a directory that is 'searched' by the console (so it has to be in %PATH% in case of Windows)
There is a package called Unipath, that does elegant, clean path calculations.
Have look here for the AbstractPath constructor
Example:
from unipath import Path
prom_dir = Path(__file__)
I have a file, for example "something.exe" and I want to find path to this file
How can I do this in python?
Perhaps os.path.abspath() would do it:
import os
print os.path.abspath("something.exe")
If your something.exe is not in the current directory, you can pass any relative path and abspath() will resolve it.
use os.path.abspath to get a normalized absolutized version of the pathname
use os.walk to get it's location
import os
exe = 'something.exe'
#if the exe just in current dir
print os.path.abspath(exe)
# output
# D:\python\note\something.exe
#if we need find it first
for root, dirs, files in os.walk(r'D:\python'):
for name in files:
if name == exe:
print os.path.abspath(os.path.join(root, name))
# output
# D:\python\note\something.exe
if you absolutely do not know where it is, the only way is to find it starting from root c:\
import os
for r,d,f in os.walk("c:\\"):
for files in f:
if files == "something.exe":
print os.path.join(r,files)
else, if you know that there are only few places you store you exe, like your system32, then start finding it from there. you can also make use of os.environ["PATH"] if you always put your .exe in one of those directories in your PATH variable.
for p in os.environ["PATH"].split(";"):
for r,d,f in os.walk(p):
for files in f:
if files == "something.exe":
print os.path.join(r,files)
Just to mention, another option to achieve this task could be the subprocess module, to help us execute a command in terminal, like this:
import subprocess
command = "find"
directory = "/Possible/path/"
flag = "-iname"
file = "something.foo"
args = [command, directory, flag, file]
process = subprocess.run(args, stdout=subprocess.PIPE)
path = process.stdout.decode().strip("\n")
print(path)
With this we emulate passing the following command to the Terminal:
find /Posible/path -iname "something.foo".
After that, given that the attribute stdout is binary string, we need to decode it, and remove the trailing "\n" character.
I tested it with the %timeit magic in spyder, and the performance is 0.3 seconds slower than the os.walk() option.
I noted that you are in Windows, so you may search for a command that behaves similar to find in Unix.
Finally, if you have several files with the same name in different directories, the resulting string will contain all of them. In consequence, you need to deal with that appropriately, maybe using regular expressions.
This is really old thread, but might be useful to someone who stumbles across this. In python 3, there is a module called "glob" which takes "egrep" style search strings and returns system appropriate pathing (i.e. Unix\Linux and Windows).
https://docs.python.org/3/library/glob.html
Example usage would be:
results = glob.glob('./**/FILE_NAME')
Then you get a list of matches in the result variable.
Uh... This question is a bit unclear.
What do you mean "have"? Do you have the name of the file? Have you opened it? Is it a file object? Is it a file descriptor? What???
If it's a name, what do you mean with "find"? Do you want to search for the file in a bunch of directories? Or do you know which directory it's in?
If it is a file object, then you must have opened it, reasonably, and then you know the path already, although you can get the filename from fileob.name too.