WHICH can help you find out which file is actually started
when you type a command.
Its syntax is simple:
WHICH program_name
program_name may be specified with or without extension.
However, do not specify a drive or path.
Wildcards and spaces are not allowed either (except in the batch
version, which does allow spaces as of version 3.00).
WHICH returns the fully qualified file name (drive:\path\filename.ext)
for the specified program name.
It searches in the same order the command processor uses:
| Download the ZIPped sources for all versions |
@ECHO OFF
:: Check Windows version
IF "%OS%"=="Windows_NT" (SETLOCAL) ELSE (GOTO Syntax)
:: Check command line argument
IF "%~1"=="" GOTO Syntax
IF NOT "%~2"=="" GOTO Syntax
ECHO.%1 | FIND /V ":" | FIND /V "\" | FIND /V "*" | FIND /V "?" | FIND /V "," | FIND /V ";" | FIND /V "/" | FIND "%~1" >NUL
IF ERRORLEVEL 1 GOTO Syntax
:: Initialize variable
SET Found=
:: Search current directory first, then PATH, for the "pure"
:: file name itself or one of the extensions defined in PATHEXT.
:: Add quotes to match directory names with spaces as well.
SET Path="%CD%";"%Path:;=";"%"
:: This command line was rewritten by Yakov Azulay.
FOR %%A IN (%Path%) DO FOR %%B IN (.;%PathExt%) DO IF EXIST "%%~A.\%~1%%~B" CALL :Found "%%~A.\%~1%%~B"
:: Display the result
ECHO.
IF DEFINED Found (ECHO.%Found%) ELSE (ECHO -None-)
:: Done
GOTO End
:Found
:: Stop after finding the first match
IF DEFINED Found GOTO:EOF
:: Store the first match found
SET Found=%~f1
GOTO:EOF
:Syntax
ECHO.
ECHO WHICH, Version 3.00
ECHO UNIX-like WHICH utility for Windows NT 4 / 2000 / XP
ECHO.
ECHO Usage: WHICH program_name
ECHO.
ECHO You may specify the program_name with or without
ECHO extension, but no wildcards, nor drive, nor path.
ECHO.
ECHO Written by Yakov Azulay
ECHO and Rob van der Woude
ECHO http://www.robvanderwoude.com
:End
IF "%OS%"=="Windows_NT" ENDLOCAL
This KiXtart version also searches for DLLs, OCXs and SYS files.
; Check command line argument
If $search = "" OR InStr( $search, "?" )
? "Which.kix, Version 1.00"
? "Locate the specified program file"
? "Port of Unix' WHICH command" ?
? "Usage: KIX32 WHICH.KIX $$SEARCH=filename[.ext]" ?
? "Where: " + Chr(34) + "filename" + Chr(34)
" is a file name only: no path, no wildcards" ?
? "Written by Rob van der Woude"
? "http://www.robvanderwoude.com" ?
Exit 2
EndIf
; Store PATH and PATHEXT in arrays
$pathArray = Split( "@CurDir;%PATH%", ";", -1 )
$pathExtArray = Split( "%PATHEXT%;.DLL;.SYS;.OCX", ";", -1 )
; Reset search result variable
$found = 0
; Search each directory until one match is found
For Each $i In $pathArray
If $found = 0
$pathDir = $i
; Strip trailing backslash and/or dot
If Right( $i, 2 ) = "\."
$pathDir = SubStr( $i, 1, Len( $i ) - 2 )
Else
If Right( $i, 1 ) = "\"
$pathDir = SubStr( $i, 1, Len( $i ) -1 )
EndIf
EndIf
; Try without appending extension first
If InStr( $search, "." )
If Exist( $pathDir + "\" + $search )
$result = $pathDir + "\" + $search
$found = 1
EndIf
Else
; Then try all the extensions from PATHEXT
For Each $j In $pathExtArray
If Exist( $pathDir + "\" + $search + $j )
$result = $pathDir + "\" + $search + $j
$found = 1
EndIf
Next
EndIf
EndIf
Next
; Display the result
If $found = 0
? "-None-" ?
$ret = 1
Else
? $result ?
$ret = 0
EndIf
; Exit with return code
Exit $ret
This Perl version also searches for DLLs, OCXs and SYS files.
#! perl
# Check command line argument(s)
if ( !@ARGV[0] or @ARGV[1] or !( @ARGV[0] =~ m/ˆ[-\w\.]+(\.\w+)?$/ ) ) {
print "\nWhich.pl, Version 1.01\n";
print "Locate the specified program file\n";
print "Port of Unix' WHICH command\n\n";
print "Usage: WHICH.PL filename[.ext]\n\n";
print "Where: \"filename\" is a file name only: no path, no wildcards\n\n";
print "Written by Rob van der Woude\n";
print "http://www.robvanderwoude.com\n";
exit(2);
}
# Store PATH and PATHEXT in arrays
while ( ( $key, $value ) = each %ENV ) {
if ( uc( $key ) eq "PATH" ) {
# Current directory must be searched before the PATH
@path = ( ".", split( /;+/,$value ) );
} elsif ( uc( $key ) eq "PATHEXT" ) {
# Search for "non-executable" program files too
@pathext = ( split( /;+/,$value ), ".DLL", ".SYS", ".OCX" );
}
}
# Search code in labelled block, to allow quitting the loop
SEARCH: {
# Search each directory specified in PATH
foreach ( @path ) {
$d = $_;
# Remove trailing backslash or backslash plus dot
$d =~ s/ˆ(.*)\\\.?$/\1/;
# Read the directory (files only)
opendir( SEARCHDIR, $d ) or die "Cannot open directory $d:\n$!";
readdir ( SEARCHDIR );
@files = grep { -f "$d\\$_" } readdir( SEARCHDIR );
foreach $file ( @files ) {
# Check for specified file name both with
# and without each extension from PATHEXT
foreach $ext ( @pathext ) {
if ( $file =~ m/ˆ@ARGV[0]($ext)?$/i ) {
# Display result
print "\n$d\\$file\n";
# Close directory handle
closedir( SEARCHDIR );
# Abort search at first successful find
last SEARCH;
}
}
}
# Close directory handle
closedir( SEARCHDIR );
}
# If you arrived here, the search was unsuccessful
print "\n-None-\n";
exit(1);
}
This PowerShell version also searches for DLLs, OCXs and SYS files.
# Which.ps1, Version 1.00
# Locate the specified program file
# Port of Unix' WHICH command
#
# Usage: WHICH.PS1 filename[.ext]
#
# Where: "filename" is a file name only: no drive or path
#
# Written by Rob van der Woude
# http://www.robvanderwoude.com
param([string] $file=$(throw "Please specify a filename."))
(get-command $file).Definition
This new WHICH version for OS/2 adds long file name support and the capability to search OS/2's LIBPATH for DLL's. Unlike Windows, OS/2 does not search its PATH for DLL's, but uses LIBPATH. Unfortunately, LIBPATH is not an environment variable, so this Rexx program has to read the LIBPATH line from CONFIG.SYS, which it looks for in the root directory of the boot drive.
/* WHICH, Version 3.00 */
/* Rexx "port" of UNIX' WHICH command */
/* that also looks for DLL's in LIBPATH */
/* Written by Rob van der Woude */
/* Initialize RexxUtil */
if RxFuncQuery( "SysLoadFuncs" ) <> 0 then do
call RxFuncAdd "SysLoadFuncs", "RexxUtil", "SysLoadFuncs"
call SysLoadFuncs
end
/* Parse command line parameters */
parse upper arg params
invalid.0 = 7
invalid.1 = "*"
invalid.2 = "?"
invalid.3 = "/"
invalid.4 = "\"
invalid.5 = ","
invalid.6 = ";"
invalid.7 = ":"
do i = 1 to invalid.0
if pos( invalid.i, params ) > 0 then call Syntax
end
parse value params with dummy1'"'longfilename'"'dummy2
if longfilename <> "" then do
if dummy1||dummy2 <> "" then call Syntax
filename = longfilename
longname = 1
end
else do
parse value params with filename dummy
if filename <> "" then do
if dummy <> "" then call Syntax
longname = 0
end
else do
call Syntax
end
end
/* Check if extension was given */
commandext.0 = 5
commandext.1 = ".BAT"
commandext.2 = ".CMD"
commandext.3 = ".COM"
commandext.4 = ".EXE"
commandext.5 = ".DLL"
addext = 1
chk4ext = right( filename, 4 )
do i = 1 to commandext.0
if chk4ext = commandext.i then addext = 0
end
/* Read PATH from environment */
path = translate( value( "PATH", , "OS2ENVIRONMENT" ) )
if chk4ext = ".DLL" then do
parse value path with ":\OS2\SYSTEM" -1 bootdrive +2
configsys = bootdrive||"\CONFIG.SYS"
call linein configsys, 1, 0
do until lines( configsys ) = 0
line = strip( translate( linein( configsys ) ) )
if left( line, 8 ) = "LIBPATH=" then do
parse value line with "LIBPATH="path
leave
end
end
end
path. = ""
path.0 = 1
path.1 = strip( directory( ), "T", "\" )||"\"
do i = 2 by 1 until path = ""
parse value path with path.i";"path
if right( path.i, 1 ) <> "\" then path.i = path.i||"\"
path.0 = i
end
/* Check if file can be found in PATH */
do i = 1 to path.0 until found.0 = 1
if addext = 0 then do
file = path.i||filename
if longname = 1 then do
file = translate( file, "?", " " )
end
call SysFileTree file, "found.", "FO"
if found.0 = 1 then do
if longname = 0 then do
call FileFound file
end
else do
file = translate( translate( file, " ", "?" ) )
if file = translate( found.1 ) then do
call FileFound file
end
end
end
end
else do j = 1 to commandext.0
file = path.i||filename||commandext.j
if longname = 1 then do
file = translate( file, "?", " " )
end
call SysFileTree file, "found.", "FO"
if found.0 = 1 then do
if longname = 0 then do
call FileFound file
end
else do
file = translate( translate( file, " ", "?" ) )
if file = translate( found.1 ) then do
call FileFound file
end
end
end
end
end
say "Not found: "||filename
EXIT 1
FileFound:
if longname = 1 then do
say 'Found: "'||arg( 1 )||'"'
end
else do
say "Found: "||arg( 1 )
end
EXIT 0
return
Syntax: procedure
say
say "WHICH, Version 3.00"
say "UNIX-like WHICH utility for OS/2 that"
say "can search for DLL's in LIBPATH as well"
say "Written by Rob van der Woude"
say
say "Usage: WHICH program_name"
say
say " or: WHICH dll_name.DLL"
say
say "You may specify program_name with or without"
say "extension. program_name as well as dll_name.DLL"
say "should be specified without a drive or path."
say
say "Spaces in long file names are allowed, wildcards"
say "aren't."
EXIT 9
return
To port WHICH.REX to Regina Rexx for Windows only 3 changes were required:
/* WHICH, Version 1.00 for Windows */
/* Rexx "port" of UNIX' WHICH command */
/* that also looks for DLL's */
/* Written by Rob van der Woude */
/* Initialize RexxUtil */
If RxFuncQuery( "SysLoadFuncs" ) <> 0 Then Do
Call RxFuncAdd "SysLoadFuncs", "RexxUtil", "SysLoadFuncs"
Call SysLoadFuncs
End
/* Display empty line */
Say
/* Parse command line parameters */
Parse Upper Arg params
invalid.0 = 7
invalid.1 = "*"
invalid.2 = "?"
invalid.3 = "/"
invalid.4 = "\"
invalid.5 = ","
invalid.6 = ";"
invalid.7 = ":"
Do i = 1 To invalid.0
If Pos( invalid.i, params ) > 0 Then Call Syntax
End
Parse Value params With dummy1'"'longfilename'"'dummy2
If longfilename <> "" Then Do
If dummy1||dummy2 <> "" Then Call Syntax
filename = longfilename
longname = 1
End
Else Do
Parse Value params With filename dummy
If filename <> "" Then Do
If dummy <> "" Then Call Syntax
longname = 0
End
Else Do
Call Syntax
End
End
/* Check if extension was given */
pathext = Translate( Value( "PATHEXT", , "ENVIRONMENT" ) )
Do i = 1 By 1 Until pathext = ""
Parse Value pathext With commandext.i";"pathext
If commandext.i <> "" Then commandext.0 = i
End
i = commandext.0 + 1
commandext.i = ".DLL"
i = commandext.0 + 2
commandext.i = ".SYS"
i = commandext.0 + 3
commandext.i = ".OCX"
commandext.0 = i
addext = 1
chk4ext = Right( filename, 4 )
Do i = 1 To commandext.0
If chk4ext = commandext.i Then addext = 0
End
/* Read PATH from environment */
path = Translate( Value( "PATH", , "ENVIRONMENT" ) )
path. = ""
path.0 = 1
/* Add current directory as first PATH entry */
path.1 = Strip( Directory( ), "T", "\" )||"\"
Do i = 2 By 1 Until path = ""
Parse Value path With path.i";"path
If Right( path.i, 1 ) <> "\" Then path.i = path.i||"\"
path.0 = i
End
/* Check if file can be found in PATH */
Do i = 1 To path.0 Until found.0 = 1
If addext = 0 Then Do
file = path.i||filename
If longname = 1 Then Do
file = Translate( file, "?", " " )
End
Call SysFileTree file, "found.", "FO"
If found.0 = 1 Then Do
If longname = 0 Then Do
Call FileFound file
End
Else Do
file = Translate( Translate( file, " ", "?" ) )
If file = Translate( found.1 ) Then Do
Call FileFound file
End
End
End
End
Else Do j = 1 To commandext.0 - 3
file = path.i||filename||commandext.j
If longname = 1 Then Do
file = Translate( file, "?", " " )
End
Call SysFileTree file, "found.", "FO"
If found.0 = 1 Then Do
If longname = 0 Then Do
Call FileFound file
End
Else Do
file = Translate( Translate( file, " ", "?" ) )
If file = Translate( found.1 ) Then Do
Call FileFound file
End
End
End
End
End
Say "Not found: "||filename
Exit 1
FileFound:
If longname = 1 Then Do
Say 'Found: "'||arg( 1 )||'"'
End
Else Do
Say "Found: "||arg( 1 )
End
Exit 0
Return
Syntax: Procedure
Say
Say "Which.rex, Version 1.00 for Regina Rexx for Windows"
Say "UNIX-like WHICH utility for Windows that can search for"
Say ".DLL, .OCX and .SYS files as well"
Say
Say "Usage: WHICH program_name"
Say
Say 'Where: "program_name" is any program name with or without extension,'
Say " but always without drive and/or path."
Say
Say "Returns: The first file with a valid or specified extension found in the PATH."
Say
Say "If program_name is specified without extension, all extensions from the PATHEXT"
Say "environment variable will be searched for."
Say "Files with .DLL, .OCX or .SYS extension will be ignored unless the extension is"
Say "specified."
Say "Spaces in long file names are allowed, wildcards aren't."
Say
Say "Written by Rob van der Woude"
Say "http://www.robvanderwoude.com"
Exit 9
Return
This VBScript version is the only version so far that checks for
internal commands first, to emulate the behaviour of the
command interpreter.
The reference list of internal commands is based on the command
interpreter specified in the %COMSPEC% environment variable
(COMMAND.COM or CMD.EXE).
This script can also be used to search for .DLL, .OCX or .PS1 files
("program" extensions without a file association).
As an added bonus, I added some command line switches to control the
output and behaviour of this script.
Read the script's on-screen help and/or its source code for details.
Option Explicit
Dim arrInt, arrPath, arrPathExt
Dim blnAll, blnClipboard, blnExtOnly, blnQuiet, blnShort
Dim i, intArgs
Dim objFSO, objIE, wshShell
Dim strComSpec, strIntAll, strIntCmd, strIntCom
Dim strPath, strPathExt, strResult
' Initialize variables
intArgs = 0
strResult = ""
' Check the command line for exactly 1 argument (the file
' name), which should NOT contain wildcard characters
If WScript.Arguments.Unnamed.Count <> 1 Then Syntax
If InStr( WScript.Arguments.Unnamed(0), "*" ) Then Syntax
If InStr( WScript.Arguments.Unnamed(0), "?" ) Then Syntax
' Check the command line switches
If WScript.Arguments.Named.Exists( "A" ) Then
blnAll = True
intArgs = intArgs + 1
Else
blnAll = False
End If
If WScript.Arguments.Named.Exists( "C" ) Then
blnClipboard = True
intArgs = intArgs + 1
Else
blnClipboard = False
End If
If WScript.Arguments.Named.Exists( "Q" ) Then
' /Q can only be used with /C
If blnClipboard Then
blnQuiet = True
intArgs = intArgs + 1
Else
Syntax
End If
Else
blnQuiet = False
End If
If WScript.Arguments.Named.Exists( "S" ) Then
blnShort = True
intArgs = intArgs + 1
Else
blnShort = False
End If
If WScript.Arguments.Named.Exists( "X" ) Then
blnExtOnly = True
intArgs = intArgs + 1
Else
blnExtOnly = False
End If
' Check for invalid command line switches
If intArgs <> WScript.Arguments.Named.Count Then Syntax
' Create the required objects
Set objFSO = CreateObject( "Scripting.FileSystemObject" )
Set wshShell = CreateObject( "Wscript.Shell" )
' Define internal command lists
strIntAll = "BREAK CALL CD CHCP CHDIR CLS COPY DATE DEL DIR ECHO ERASE " _
& "EXIT FOR GOTO IF MD MKDIR MOVE PATH PAUSE PROMPT RD REM " _
& "REN RENAME RMDIR SET SHIFT TIME TYPE VER VERIFY VOL "
strIntCmd = "ASSOC COLOR ENDLOCAL FTYPE POPD PUSHD SETLOCAL START TITLE"
strIntCom = "CTTY LFNFOR LH LOADHIGH LOCK TRUENAME UNLOCK"
' Determine the type of command processor
' used: COMMAND.COM or CMD.EXE
strComSpec = UCase( wshShell.ExpandEnvironmentStrings( "%COMSPEC%" ) )
If Right( strComSpec, 12 ) = "\COMMAND.COM" Then
arrInt = Split( strIntAll & strIntCom )
End If
If Right( strComSpec, 8 ) = "\CMD.EXE" Then
arrInt = Split( strIntAll & strIntCmd )
End If
' Read the PATH and PATHEXT variables, and store their values in
' arrays; the current directory is prepended to the PATH first
strPath = wshShell.CurrentDirectory & ";" _
& wshShell.ExpandEnvironmentStrings( "%PATH%" )
strPathExt = wshShell.ExpandEnvironmentStrings( "%PATHEXT%" )
arrPath = Split( strPath, ";" )
arrPathExt = Split( strPathExt, ";" )
' Check if the command line argument contains a dot
' which would mean we wouldn't have to use PATHEXT
If InStr( WScript.Arguments.Unnamed(0), "." ) = 0 Then
' But first let's check for INTERNAL commands, unless of course, the /X switch was used
If IsArray( arrInt ) And Not blnExtOnly Then
For i = 0 To UBound( arrInt )
If UCase( WScript.Arguments.Unnamed(0) ) = arrInt(i) Then
strResult = "[" & wshShell.ExpandEnvironmentStrings( "%COMSPEC%" ) _
& "]::" & arrInt(i)
Exit For
End If
Next
End If
If strResult = "" Then
' Use list of valid extensions from PATHEXT if
' no extension was specified on the command line
For i = 0 To UBound( arrPathExt )
' Search the PATH
strResult = strResult _
& FindWhich( WScript.Arguments.Unnamed(0) & arrPathExt(i) )
' Unless the /A (All) command line switch was
' used, abort when the first file is found
If Not blnAll And strResult <> "" Then
Exit For
End If
Next
End If
Else
' If an extension WAS specified, just search the PATH
strResult = FindWhich( WScript.Arguments.Unnamed(0) )
End If
' Copy the result to clipboard if the /C command line switch was used
If blnClipboard Then
Set objIE = CreateObject( "InternetExplorer.Application" )
objIE.Navigate( "about:blank" )
objIE.Document.ParentWindow.ClipboardData.SetData "text", strResult
objIE.Quit
Set objIE = Nothing
End If
' Display the result, unless the /Q command line switch was used
If Not blnQuiet Then
WScript.Echo strResult
End If
Function FindWhich( myFile )
' This function searches the directories in
' the PATH array for the specified file name
Dim i, objFound, strFound, strFullPath, strTestPath
strFound = ""
For i = 0 To UBound( arrPath )
' Skip empty directory values, caused by the PATH
' variable being terminated with a semicolon
If arrPath(i) <> "" Then
' Build a fully qualified path of the file to test for
strTestPath = objFSO.BuildPath( arrPath(i), myFile )
' Check if that file exists
If objFSO.FileExists( strTestPath ) Then
If blnShort Then
' Get the capitalization right
strTestPath = objFSO.GetAbsolutePathName( strTestPath )
' Return the short full path
Set objFound = objFSO.GetFile( strTestPath )
strFullPath = objFound.ShortPath
Set objFound = Nothing
Else
' Return the full path with proper capitalization
strFullPath = objFSO.GetAbsolutePathName( strTestPath )
End If
If blnAll Then
' Append the path of the file found
strFound = strFound & ";" _
& strFullPath
Else
' Abort when the first file is found
strFound = strFullPath
Exit For
End If
End If
End If
Next
If blnAll And strFound <> "" Then
strFound = Replace( Mid( strFound, 2 ), ";", vbCrLf ) & vbCrLf
End If
FindWhich = strFound
End Function
Sub Syntax( )
Dim strMsg
strMsg = vbCrLf _
& "Which.vbs, Version 1.10" & vbCrLf _
& "Find out which file or internal command is actually executed when you type" _
& vbCrLf _
& "a command without its fully qualified path (like UNIX' which command)" _
& vbCrLf & vbCrLf _
& "Usage: WHICH.VBS filename[.ext] [ /A ] [ /C [ /Q ] ] [/S] [/X]" _
& vbCrLf & vbCrLf _
& "Where: filename[.ext] the file name with optional extension to search for;" _
& vbCrLf _
& " wildcard characters (""*"" and ""?"") are NOT allowed;" _
& vbCrLf _
& " use the extension to search for .dll, .ocx, .ps1, etc." _
& vbCrLf _
& " /A list All files found instead of only the first match" _
& vbCrLf _
& " (ignored if a matching internal command is found)" _
& vbCrLf _
& " /C copy result to the Clipboard" _
& vbCrLf _
& " /Q Quiet mode, no screen output (only valid with /C)" _
& vbCrLf _
& " /S return Short path(s) (8.3 notation)" _
& vbCrLf _
& " /X eXternal commands only, ignore internal commands" _
& vbCrLf & vbCrLf _
& "Written by Rob van der Woude" & vbCrLf _
& "http://www.robvanderwoude.com" & vbCrLf
WScript.Echo strMsg
WScript.Quit 1
End Sub
I added this DOS version just for fun, to show the difference
between "real" and "enhanced" DOS versions.
SLE stands for Severely Limited Edition.
It doesn't take an extremely long PATH variable for this batch
file to encounter the 127 character limit on command lines,
posed by COMMAND.COM.
@ECHO OFF
:: Command line error check:
IF "%1"=="" GOTO Syntax
:: Check PATH length:
SET TEST=
FOR %%A IN (.\;%PATH%) DO SET TEST=OK
CLS
IF NOT "%TEST%"=="OK" GOTO StrLength
:: The actual program:
ECHO.
FOR %%A IN (.\;%PATH%) DO IF EXIST %%A.\%1.BAT TRUENAME %%A.\%1.BAT
FOR %%A IN (.\;%PATH%) DO IF EXIST %%A.\%1.EXE TRUENAME %%A.\%1.EXE
FOR %%A IN (.\;%PATH%) DO IF EXIST %%A.\%1.COM TRUENAME %%A.\%1.COM
FOR %%A IN (.\;%PATH%) DO IF EXIST %%A.\%1 TRUENAME %%A.\%1
GOTO End
:: PATH length error message:
:StrLength
ECHO.
ECHO Sorry, your PATH environment variable is too long to
ECHO be handled by this batch file.
ECHO.
ECHO The 127 character command line limit was exceeded
ECHO when just testing.
GOTO End
:: Help screen:
:Syntax
ECHO.
ECHO WHICH, Version 1.00
ECHO UNIX-like WHICH utility for DOS
ECHO Written by Rob van der Woude
ECHO.
ECHO Usage: WHICH program_name
ECHO.
ECHO You may specify program_name with or without extension,
ECHO but without a drive or path.
ECHO Spaces or wildcards aren't allowed either.
ECHO.
ECHO Limitation: This batch file has problems handling long
ECHO PATH variables; it will say so, however,
ECHO when it encounters this problem.
:End
ECHO.
SET TEST=