I often like to find the location of an executable file or a script while I'm at the command line. In some cases, different versions of utilities might exist in one or more directories, affecting which version of a program runs when you type its name at a command prompt. This problem could also affect which programs your scripts run if your scripts call executables without specifying a path.
UNIX-like OSs commonly have a lot of directories in the search path— the list of directories the system searches when looking for executables—and it's not always clear which directory contains a particular program. So that you don't have to look through multiple directories, the UNIX-like OSs typically provide a which command that takes an executable name as an argument and returns the path and filename of the executable. Windows lacks such a command, so I decided to fill the gap by writing a JScript script—Which.js— to do the same thing.
Which.js requires Windows Script Host (WSH) 5.6 (which you already have installed if you have Internet Explorer 6.0 or later). The script uses the command-line syntax
where executable is the program or script you want to find in the system path. You can also optionally provide the program or script's file extension.
As a concrete example of how you might use Which.js, suppose you have a script that uses the Diruse utility, such as the Du.cmd script from "Short Shell Script Supplies Diruse.exe Arguments" (InstantDoc ID 50582). The script works on your development machine but fails on another machine because diruse.exe isn't installed there. You can run the command
on the development machine to find the full path of diruse.exe.
The PATH and PATHEXT Variables
Before walking through Which.js, let's review the Windows command-shell PATH and PATHEXT variables, which the script uses to build the directory and extension arrays it searches to find the executable you specify. The PATH variable contains a semicolon-delimited list of directory names. If a program or script doesn't exist in the current directory, Windows appends the executable's filename to the first directory in the path. If the executable doesn't exist there, it looks in the second directory, and so forth.
Windows constructs the path based on the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path and HKEY_CURRENT _USER\Environment\Path registry subkeys. The first value contains the system path. Any user who logs onto the computer has directories listed in the system path. The second subkey contains the current user path. The directories in this path apply only to the current user. When an application or script requests the contents of the PATH environment variable, Windows appends the user path to the end of the system path, so the system always searches the system path first.
The PATHEXT variable is a semicolon-delimited list of file extensions the system considers executable. In this context, the term executable means a directly executable program (i.e., a file with the .exe extension) or a script that's executed indirectly by another program. For example, a common PATHEXT variable contains the following string:
The first two extensions—.com and .exe—are directly executable by the OS. Other programs execute the rest of the extensions in this list; cmd.exe executes .bat and .cmd files, and WSH executes the remaining extensions. You might see other extensions in this list on your system if you've installed other programming languages such as Perl or Python. Which.js uses this list of extensions if you don't type an extension name.
Understanding the Script
Listing 1 shows the Which.js script. It begins by declaring the SCRIPT_NAME variable, which the usage function uses to display the script's name. The script then executes the Quit method of the WScript object to exit the script, using the main function as an argument. This means the main function's exit code will be the script's exit code.
Now, let's go inside the script's main function, which first declares a set of variables. It then determines whether the script was executed without command-line arguments or the /? argument is present. If either of these conditions is true, the main function executes the usage function, which displays a usage message and ends the script.
At callout A, the main function creates an easy way to retrieve environment variables. It uses the Environment property of the WScript.Shell object to return the WshEnvironment object, which contains a collection of environment variables. The WshEnvironment object accepts an optional argument that specifies the location of the environment variables you want to access. The argument can be one of four values: System, User, Volatile, or Process. Which.js retrieves the PATH variable from the current process, so it passes the string "Process" as the argument for the WshEnvironment object.
The main function uses the WshEnvironment object's Item method with the environment variable's name as an argument. The Item method returns a string containing the environment variable's contents. At callout B, the main function retrieves the contents of the PATH environment variable. The function uses the split method to return an array of substrings. The split method's argument is a string or regular expression that determines how to divide the string into array elements. As I explained earlier, the PATH environment variable is a semicolon-delimited list of directory names, so the main function passes a semicolon as the argument for the split method. The result is that each item in the array is a directory in the path.
Windows searches for executables in the current directory before it searches the directories listed in the PATH environment variable, so the main function inserts the current directory at the front of the array by using the unshift method, as the code at callout C shows. The function retrieves the full path to the current directory by passing a single dot (.) as the argument to the GetAbsolute-PathName method of the FileSystemObject object.
Next, the main function uses a for loop to iterate through the directories in the array. If the executable's name (i.e., the name typed on the command line) doesn't include a file extension, the main function creates a second array containing the list of semicolon-separated executable file extensions from the PATHEXT environment variable. The main function iterates through this array with a second for loop, using the FileSystemObject object's BuildPath method to append each extension in turn to the executable name. The function then determines whether the file exists by using the FileSystemObject object's FileExists method. If the file exists, the function uses the break statement to exit the for loops. The first break statement exits from iterating through the array of extensions in the PATHEXT variable, and the second break statement exits from iterating through the directories in the PATH variable.
If the executable's name includes an extension, the main function just iterates through the array of directories, using the FileSystemObject object's BuildPath method to append the executable's name to each directory in the array. The function then uses the FileSystemObject object's FileExists method to determine whether the file exists. If the file exists, the function uses the break statement to exit from the for loop.
At this point, if the script didn't find the executable, the main function's file variable contains an empty string and the main function displays a message to that effect and returns a return value of 2 (ERROR_FILE_NOT_FOUND). Otherwise, the script displays the name of the executable it found.
Do You Know Where Your Programs Are?
Any systems administrator who frequently works at a command prompt will find this utility valuable. Microsoft might not have included such fundamental functionality, but you no longer have to wonder where your executables and scripts are located in the file system. Just let Which.js find them for you.