Ron
Ron

Reputation: 2503

Python script errors out

I have this script, which I have no doubt is flawed:

import fnmatch, os, sys
def findit (rootdir, find, pattern):
    for folder, dirs, files in os.walk(rootdir):
        print (folder)
    for filename in fnmatch.filter(files,pattern):          
        with open(filename) as f:
            s = f.read()
            f.close()
            if find in s :                  
                print(filename)

findit(sys.argv[1], sys.argv[2], sys.argv[3])

when I run it I get Errno2, no such file or directory. BUT the file exists. For instance if I execute it by going: findit.py c:\python "folder" *.py it will work just fine, listing all the *.py files which contain the word "folder". BUT if I go findit.py c:\php\projects1 "include" *.php

as an example I get [Errno2] no such file or directory: 'About.php' (for example). But About.php exists. I don't understand what it's doing, or what I'm doing wrong.

Upvotes: 1

Views: 71

Answers (2)

abarnert
abarnert

Reputation: 365617

If you look at any of the examples for os.walk, you'll see that they all do os.path.join(root, name). You need to do that too.

Why? Quoting from the docs:

filenames is a list of the names of the non-directory files in dirpath. Note that the names in the lists contain no path components. To get a full path (which begins with top) to a file or directory in dirpath, do os.path.join(dirpath, name).

If you just use the filename as a path, it's going to look for a file of the same name in the current working directory. If there's no such file, you'll get a FileNotFoundError. If there is such a file, you'll open and read the wrong file. Only if you happen to be looking inside the current working directory will it work.


There's also another major problem in your code: os.walk walks a directory tree recursively, finding all files in the given top directory, or any subdirectory of top, or any subdirectory of… and so on, yielding once for each directory. But you're not doing anything useful with that (except printing out the folders). Instead, you wait until it finishes, and then use the files from whichever directory it happened to reach last.

If you just want to get a flat listing of the files directly in a directory, use os.listdir, not os.walk. (Or maybe use glob.glob instead of explicitly listing everything then filtering with fnmatch.)

On the other hand, if you want to walk the tree, you have to move your second for loop inside the first one.


You've also got a minor problem: You call f.close() inside a with open(…) as f:, which leads to f being closed twice. This is guaranteed to be completely harmless (at least in 2.5+, including 3.x), but it's still a bad idea.


Putting it together, here's a working version of your code:

def findit (rootdir, find, pattern):
    for folder, dirs, files in os.walk(rootdir):
        print (folder)
        for filename in fnmatch.filter(files,pattern):
            pathname = os.path.join(folder, filename)
            with open(pathname) as f:
                s = f.read()
                if find in s:
                    print(pathname)

Upvotes: 2

David Heffernan
David Heffernan

Reputation: 612794

You are using a relative filename. But your current directory does not contain the file. And you don't want to search there anyway. Use os.path.join(folder, filename) to make an absolute path.

Upvotes: 1

Related Questions