A dos version of unix which command

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
mich
Posts: 8
Joined: 22 Sep 2011 00:19

A dos version of unix which command

#1 Post by mich » 03 Jan 2012 10:17

I often find myself using a different perl.exe of java.exe then I intended to use. This made me look if there was a dos replacement of the unix which command.

For those unaware of this command, which perl, would simply find the first perl in the PATH. In that sense it is also a nice tool to see if a command you need is actually available.

As usual Google lend me a hand and showed me this:

Code: Select all

@echo off
rem --------------------------------------------------------
rem File: which.cmd
rem Description: Windows equivalent of Unix which command
rem Author: Pankaj Kumar
rem Copyright 2004 Pankaj Kumar. All Rights Reserved.
rem License: This software is available under GPL (http://www.gnu.org/licenses/gpl.html)
rem ---------------------------------------------------------
setlocal
if "%1" == "" goto noArg

set fullpath=%~$PATH:1
if "%fullpath%" == "" goto notFound
echo Found in PATH: %fullpath%
goto end

:noArg
echo No Argument specified
goto end

:notFound
echo Argument "%1" not found in PATH

:end
endlocal


While this works OK when I know if I'm looking for a .exe or a .com file, I would like this batch to use the PATHEXT variable so that I only need to specify the name of the executable and not its extension.

I guess I can chop PATHEXT into pieces using something like this:

Code: Select all

FOR /F  "delims=;" a%% in ("%PATHEXT%") do call:a_function %%a


But before I can work on that, I need to understand what this here actually does:

Code: Select all

set fullpath=%~$PATH:1


And now I just realised I don't understand how the set command works :oops:

So my "perl.exe" is inside %1, and then suddenly fullpath becomes c:\perl\bin\perl.exe

I really like to understand how this works.
Last edited by mich on 03 Jan 2012 15:49, edited 1 time in total.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: A dos version of unix which command

#2 Post by Squashman » 03 Jan 2012 11:30

I think you would want to do it this way.

Code: Select all

set myext=%PATHEXT:;= %
FOR %%a in (%myext%) do call :function "%~1%%~a"

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: A dos version of unix which command

#3 Post by Squashman » 03 Jan 2012 11:49

Not sure why I did that first set statement. You can do the string replacement in the FOR LOOP.

Code: Select all

FOR %%a in (%PATHEXT:;= %) do call :function "%~1%%~a"

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: A dos version of unix which command

#4 Post by Aacini » 03 Jan 2012 19:53

I wrote this Batch file that seek for an executable file in the same order CMD.EXE do:

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM CREATE A LIST OF FILE NAMES ADDING THE EXECUTABLE EXTENSIONS
SET NAMEEXT=!PATHEXT:.=%1.!
REM SEARCHES FILE NAMES IN CURRENT DIRECTORY, IF FOUND: ERRORLEVEL=1
FOR %%N IN (%NAMEEXT%) DO IF EXIST %%N ECHO %%N & EXIT /B 1
REM SEARCHES FILE NAMES IN DIRECTORIES OF PATH VARIABLE, IF FOUND: ERRORLEVEL=2
FOR %%N IN (%NAMEEXT%) DO IF NOT "%%~$PATH:N" == "" ECHO %%~$PATH:N & EXIT /B 2
REM IF FILE NOT FOUND, ERRORLEVEL=0
ECHO '%1' is not an external command or batch file located in PATH & EXIT /B 0
I called it PATHOF.BAT, for example:

Code: Select all

PATHOF EDIT


PS - I don't like "which" name, it is unclear and ambiguous (which what?).

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: A dos version of unix which command

#5 Post by Squashman » 03 Jan 2012 21:00

I have always been partial to WHEREIS

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: A dos version of unix which command

#6 Post by Squashman » 03 Jan 2012 21:20

:?:
Shouldn't this code find the file in all directories within your path? It only seems to find it in the first directory within the path.

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET NAMEEXT=!PATHEXT:.=%1.!
FOR %%N IN (%NAMEEXT%) DO IF NOT "%%~$PATH:N" == "" ECHO %%~$PATH:N

Notepad.exe exists in C:\Windows\System32 and C:\Windows. Since System32 is the first folder in my path it finds it there but not in C:\Windows

If I do have notepad.bat in another directory it does find Notepad.exe and Notepad.bat

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: A dos version of unix which command

#7 Post by dbenham » 03 Jan 2012 22:05

Squashman wrote:It only seems to find it in the first directory within the path.
That is precisely the expected behavior. The whole point is to determine what will be executed if an attempt is made to invoke a given executable such as "notepad" from the current directory.

@Aacini
The supplied command name could include special characters like <space>(unlikely) and/or a path (more likely) and/or an extension (more likely).
The definition of NAMEEXT could be modified to account for the above:

Code: Select all

set NAMEEXT="%~1";!PATHEXT:.="%~1".!
Also, if the file is found in the current directory search, then should echo %%~fN instead %%N.

The only remaining problem I see is if %1 contains ! (very unlikely)

Dave Benham

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: A dos version of unix which command

#8 Post by Squashman » 03 Jan 2012 22:13

dbenham wrote:That is precisely the expected behavior. The whole point is to determine what will be executed if an attempt is made to invoke a given executable such as "notepad" from the current directory.

I suppose that does make sense. For some reason I was thinking he did want to find the paths to all versions of JAVA within the path.

Couple years ago I had installed some unix utilities on my computer and put the path to the binaries in my PATH. Forgot that I had done that until I ended up using a command with the same name as one that already existed in Windows.

mich
Posts: 8
Joined: 22 Sep 2011 00:19

Re: A dos version of unix which command

#9 Post by mich » 05 Jan 2012 00:44

Thanks everybody,

This site really is a place to get answers :-)

The idea is indeed to get the location of the first matching executable.

Having said that, I am actually often interested in what 'else' is there, and where. That would show, as usual, that someone installed or upgraded something, putting their executable before mine, in the path.

Thanks again,

mich

mich
Posts: 8
Joined: 22 Sep 2011 00:19

Re: A dos version of unix which command

#10 Post by mich » 05 Jan 2012 00:57

Squashman wrote:Not sure why I did that first set statement. You can do the string replacement in the FOR LOOP.


Never worry about that. I made a 60 line perl script for a customer about 2 years ago. Some consultant said it was crap and replaced it by a 10 line version, doing the same. Yesterday I found they reused my script again when they needed to make some minor changes. They could not figure out how the other script worked!

:)

mich

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: A dos version of unix which command

#11 Post by Liviu » 21 Feb 2014 19:59

There is a problem with the proposed approach, which I think is significant even if maybe less common. Issue is that the code checks each extension in PATHEXT against every directory in the PATH (with the exception of the current directory being checked first). While cmd checks each directory in the PATH for each extension in PATHEXT (http://technet.microsoft.com/en-us/library/cc723564.aspx#XSLTsection127121120120: "the shell tries all possible file extensions in a specific directory before moving on to search the next directory").

For an example, suppose that PATH=C:\myPath;C:\winPath and files C:\myPath\etc.bat, C:\winPath\etc.exe exist. The posted code will return C:\winPath\etc.exe as a match (assuming the default PATHEXT with EXE priority higher than BAT), but cmd would actually execute C:\myPath\etc.bat when typing in 'etc' at the prompt.

Below is an alternative approach which I believe follows cmd's search logic correctly. EDIT: update posted under http://www.dostips.com/forum/viewtopic.php?p=32653#p32653 below, which fixes the wrong \nul directory check

Code: Select all

:: pathsOf.cmd
@echo off & setlocal disableDelayedExpansion

@rem current directory is searched first
set "PATHS=%CD%;%PATH%"
@rem exact match checked first if extension specified
if "%~x1" neq "" (set "PATHEXTS=;%PATHEXT%") else (set "PATHEXTS=%PATHEXT%")

set/a ret=0
for %%P in ("%PATHS:;=";"%") do (
  for %%X in ("%PATHEXTS:;=";"%") do (
    @rem target must be checked not to be a directory, otherwise
    @rem 'c:\windows\help' would be returned as a match for 'pathsOf help'
    if exist "%%~P\%~1%%~X" if not exist "%%~P\%~1%%~X\nul" (
      set/a ret+=1
      @rem extra step to display the correct capitalization of
      @rem the actual file on disk e.g. 'notepad.exe' vs 'notepad.EXE'
      for %%F in ("%%~P\%~1%%~X") do (
        echo %%~fF
) ) ) )

if %ret%==1 (set/a ret=0) else if %ret%==0 (set/a ret=1) else (set/a ret=-1)
@rem return 1 for no match, 0 for single match, -1 for multiple matches
@rem so that 'if not errorlevel 1' verifies that the file can be run
@rem and the first line of output gives the full path of the file to run
endlocal & exit /b %ret%

Liviu
Last edited by Liviu on 21 Feb 2014 22:27, edited 1 time in total.

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: A dos version of unix which command

#12 Post by carlos » 21 Feb 2014 20:15

good code Liviu. It Works very well and also accept wildcards:

C:\dev>whereis java
C:\Windows\System32\java.exe
C:\Program Files (x86)\Java\jre7\bin\java.exe
C:\Program Files\Java\jdk1.6.0_35\bin\java.exe

C:\dev>whereis jav*
C:\Windows\System32\java.exe
C:\Windows\System32\javaw.exe
C:\Windows\System32\javaws.exe
C:\Program Files (x86)\Java\jre7\bin\java-rmi.exe
C:\Program Files (x86)\Java\jre7\bin\java.exe
C:\Program Files (x86)\Java\jre7\bin\javacpl.exe
C:\Program Files (x86)\Java\jre7\bin\javaw.exe
C:\Program Files (x86)\Java\jre7\bin\javaws.exe
C:\Program Files\Java\jdk1.6.0_35\bin\java-rmi.exe
C:\Program Files\Java\jdk1.6.0_35\bin\java.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javac.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javadoc.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javah.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javap.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javaw.exe
C:\Program Files\Java\jdk1.6.0_35\bin\javaws.exe



a question: why you use

Code: Select all

if not exist "%%~P\%~1%%~X\nul
? maybe it sufficient with

Code: Select all

if not exist "%%~P\%~1%%~X\

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: A dos version of unix which command

#13 Post by Liviu » 21 Feb 2014 22:25

carlos wrote:It Works very well and also accept wildcards
The part about wildcards was completely accidental ;-) and didn't actually work consistently, since it had the check for directories in the wrong place, outside the innermost for loop. The updated code should fix that particular issue, but I'll leave it at "unsupported" status - it never was my intention for it to work at all, and I can imagine there might be other issues outstanding, but codewise it's easier to just leave it in than try to catch and rule out wildcard'ed input.

carlos wrote:a question: why you use

Code: Select all

if not exist "%%~P\%~1%%~X\nul
? maybe it sufficient with

Code: Select all

if not exist "%%~P\%~1%%~X\
Good question, thanks for asking ;-) Short answer must be that it's a hard-to-break old habit from way back in the DOS days. I've known for some time that the \nul check doesn't work reliably nowadays (for example 'if exist "%temp%\nul" echo it's a dir' fails). Incidentally, the reason why the original code I posted didn't return 'c:\windows\help' as a match for 'pathsOf help' was not the directory check (which in fact failed), but because of cmd's other odd rule that extension-less filenames such as 'help' are not checked literally, first.

Below is an update that fixes the directory check.

Code: Select all

:: pathsOf.cmd
@echo off & setlocal disableDelayedExpansion

@rem current directory is searched first
set "PATHS=%CD%;%PATH%"
@rem exact match checked first if extension specified
if "%~x1" neq "" (set "PATHEXTS=;%PATHEXT%") else (set "PATHEXTS=%PATHEXT%")

set/a ret=0
for %%P in ("%PATHS:;=";"%") do (
  for %%X in ("%PATHEXTS:;=";"%") do (
    if exist "%%~P\%~1%%~X" (
      @rem extra step to display the correct capitalization of
      @rem the actual file on disk e.g. 'notepad.exe' vs 'notepad.EXE'
      for %%F in ("%%~P\%~1%%~X") do (
        @rem target must still be checked not to be a directory, otherwise
        @rem 'c:\windows\help' would be returned as a match for 'pathsOf help'
        @rem %%~a leftmost char is 'd' for a directory vs '-' for files
        @rem ascii '-' < 'd' so the following filters out 'd...' directories
        if /i "%%~aF" lss "d" (
          set/a ret+=1
          echo %%~fF
) ) ) ) )

if %ret%==1 (set/a ret=0) else if %ret%==0 (set/a ret=1) else (set/a ret=-1)
@rem return 1 for no match, 0 for single match, -1 for multiple matches
@rem so that 'if not errorlevel 1' verifies that the file can be run
@rem and the first line of output gives the full path of the file to run
endlocal & exit /b %ret%

Liviu

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: A dos version of unix which command

#14 Post by carlos » 21 Feb 2014 23:16

thanks Liviu for the explanation, and works ok. I save this code in my archive.

Post Reply