Friday, March 27, 2015

Which which is which?

By Vasudev Ram

Recently I had blogged about, a simple Python program that I wrote, here:

A simple UNIX-like 'which' command in Python

I also posted the same program on ActiveState Code:

A UNIX-like "which" command for Python (Python recipe)

A reader there, Hongxu Chen, pointed out that my actually implemented the variant "which -a", that is, the UNIX which with the -a (or -all) option included. This variant displays not just the first full pathname of an occurrence of the searched-for name in the PATH (environment variable), but all such occurrences, in any directory in the PATH. That was not what I had intended. It was a bug. I had intended it to only show the first occurrence.

So I rewrote the program to fix that bug, and also implemented the -a option properly - i.e. when -a (or its long form, --all, is given, find all occurrences, otherwise only find the first. Here is the new version:
from __future__ import print_function

# A minimal version of the UNIX which utility, in Python.
# Also implements the -a or --all option.
# Author: Vasudev Ram -
# Copyright 2015 Vasudev Ram -

import sys
import os
import os.path
import stat

def usage():
    sys.stderr.write("Usage: python [ -a | --all ] name ...\n") 
    sys.stderr.write("or: [ -a | --all ] name ...\n") 

def which(name, all):
    for path in os.getenv("PATH").split(os.path.pathsep):
        full_path = path + os.sep + name
        if os.path.exists(full_path):
            if not all:

def main():
    if len(sys.argv) < 2:
    if sys.argv[1] in ('-a', '--all'):
        # Print all matches in PATH.
        for name in sys.argv[2:]:
            which(name, True)
        # Stop after printing first match in PATH.
        for name in sys.argv[1:]:
            which(name, False)

if "__main__" == __name__:
I tested it some and it seems to be working okay both with and without the -a option now. After more testing, I'll upload it to my Bitbucket account.

- Vasudev Ram - Online Python training and programming

Dancing Bison Enterprises

Signup to hear about new software or info products that I create.

Posts about Python  Posts about xtopdf

Contact Page


Anonymous said...

Your does not check if the file it has found is really a file (as opposed to a dir) and has executable permissions.
So run this: mkdir /usr/bin/spamm; touch /usr/local/bin/spamm
Then " spamm" should give nothing, but gives:

Vasudev Ram said...

Thanks for your comment. You are right about those checks (for a dir or an exe file) not being done. But did you read the previous post that I linked to:

A simple UNIX-like "which" command in Python

in which (pun intended:) I said:

"I'll discuss my interpretation of these variations in an upcoming post - including a variation that uses os.stat(full_path).st_mode:"?

If you look at that commented code fragment, you'll see that it includes a check for if a file is executable, i.e.:

if os.stat(full_path).st_mode & stat.S_IXUSR:

And I have not yet written that "upcoming post". However, I had not thought of checking for the file being a directory as well (good point), which I will now do in that next post, including trying to take care of the case where a file can be a dir (with execute bit set), and excluding that case (at least on UNIX, need to see if Python supports checking for that on Windows).