Saturday, December 3, 2016

Simple directory lister with multiple wildcard arguments

By Vasudev Ram

$ python file_glob.py f[!2-5]*xt

I was browsing the Python standard library (got to use those batteries!) and thought of writing this simple utility - a command-line directory lister that supports multiple wildcard filename arguments, like *, ?, etc. - as the OS shells bash on Unix and CMD on Windows do. It uses the glob function from the glob module. The Python documentation for glob says:

[ No tilde expansion is done, but *, ?, and character ranges expressed with [] will be correctly matched. This is done by using the os.listdir() and fnmatch.fnmatch() functions in concert. ]

Note: no environment variable expansion is done either, but see os.path.expanduser() and os.path.expandvars() in the stdlib.

I actually wrote this program just to try out glob (and fnmatch before it), not to make a production or even throwaway tool, but it turns out that, due to the functionality of glob(), even this simple program is somewhat useful, as the examples of its use below show, particularly with multiple arguments, etc.

Of course, this is not a full-fledged directory lister like DIR (Windows) or ls (Unix) but many of those features can be implemented in this or similar tools, by using the stat module (which I've used in my PySiteCreator and other programs); the Python stat must be a wrapper over the C library with the same name, at least on Unix (AFAIK the native Windows SDK's directory and file system manipulation functions are different from POSIX ones, though the C standard library on Windows has many C stdio functions for compatibility and convenience).

Here is the code for the program, file_glob.py:
from __future__ import print_function
'''
file_glob.py
Lists filenames matching one or more wildcard patterns.
Author: Vasudev Ram
Copyright 2016 Vasudev Ram
Web site: https://vasudevram.github.io
Blog: http://jugad2.blogspot.com
Product store: https://gumroad.com/vasudevram
'''

import sys
import glob

sa = sys.argv
lsa = len(sys.argv)

if lsa < 2:
    print("{}: Must give one or more filename wildcard arguments.".
        format(sa[0]))
    sys.exit(1)

for arg in sa[1:]:
    print("Files matching pattern {}:".format(arg))
    for filename in glob.glob(arg):
            print(filename)

I ran it multiple times with these files in my current directory. All of them are regular files except dir1 which is a directory.
$ dir /b
dir1
f1.txt
f2.txt
f3.txt
f4.txt
f5.txt
f6.txt
f7.txt
f8.txt
f9.txt
file_glob.py
o1.txt
out1
out3
test_fnmatch1.py
test_fnmatch2.py
test_glob.py~
Here are a few different runs of the program with 0, 1 or 2 arguments, giving different outputs based on the patterns used.
$ python file_glob.py
Must give one or more filename wildcard arguments.
$ python file_glob.py a
Files matching pattern a

$ python file_glob.py *1
Files matching pattern *1:
dir1
out1
$ python file_glob.py *txt
Files matching pattern *txt:
f1.txt
f2.txt
f3.txt
f4.txt
f5.txt
f6.txt
f7.txt
f8.txt
f9.txt
o1.txt
$ python file_glob.py *txt *1
Files matching pattern *txt:
f1.txt
f2.txt
f3.txt
f4.txt
f5.txt
f6.txt
f7.txt
f8.txt
f9.txt
o1.txt
Files matching pattern *1:
dir1
out1
$ python file_glob.py *txt *py
Files matching pattern *txt:
f1.txt
f2.txt
f3.txt
f4.txt
f5.txt
f6.txt
f7.txt
f8.txt
f9.txt
o1.txt
Files matching pattern *py:
file_glob.py
test_fnmatch1.py
test_fnmatch2.py
$ python file_glob.py f[2-5]*xt
Files matching pattern f[2-5]*xt:
f2.txt
f3.txt
f4.txt
f5.txt
$ python file_glob.py f[!2-5]*xt
Files matching pattern f[!2-5]*xt:
f1.txt
f6.txt
f7.txt
f8.txt
f9.txt
$ python file_glob.py *mat*
Files matching pattern *mat*:
test_fnmatch1.py
test_fnmatch2.py
$ python file_glob.py *e* *[5-8]*
Files matching pattern *e*:
file_glob.py
test_fnmatch1.py
test_fnmatch2.py
test_glob.py~
Files matching pattern *[5-8]*:
f5.txt
f6.txt
f7.txt
f8.txt
$ python file_glob.py *[1-4]*
Files matching pattern *[1-4]*:
dir1
f1.txt
f2.txt
f3.txt
f4.txt
o1.txt
out1
out3
test_fnmatch1.py
test_fnmatch2.py
$ python file_glob.py a *txt b
Files matching pattern a:
Files matching pattern *txt:
f1.txt
f2.txt
f3.txt
f4.txt
f5.txt
f6.txt
f7.txt
f8.txt
f9.txt
o1.txt
Files matching pattern b:
As you can see from the runs, it works, including for ranges of wildcard characters, and the negation of them too (using the ! character inside the square brackets before the range).

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates on my software products / ebooks / courses.

Jump to posts: Python   DLang   xtopdf

Subscribe to my blog by email

My ActiveState recipes

Managed WordPress Hosting by FlyWheel


1 comment:

Vasudev Ram said...


My apologies to people reading this via Planet Python, who may see the post twice - once now, and once after the next refresh of the planet's feed. This may happen because this time I put the "Python" label on the post after the first publish, and then went on to make a few last minute edits before re-publishing. So it got picked up too early. Those edits (mainly mention of PySiteCreator) will be seen in the next refresh. Usually I put that label last, after doing all the editing and re-editing ...