A 90-byte “whereis” program

Date:January 20, 2005 / year-entry #18
Tags:tipssupport
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20050120-00/?p=36653
Comments:    41
Summary:Sometimes people try too hard. You can download a C# program to look for a file on your PATH, or you can use a 90-character batch file: @for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if NOT "%%~$PATH:i"=="" echo %%~$PATH:i

Sometimes people try too hard.

You can download a C# program to look for a file on your PATH, or you can use a 90-character batch file:

@for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if NOT "%%~$PATH:i"=="" echo %%~$PATH:i

Comments (41)
  1. David Heffernan says:

    This post would be better if the batch file actually worked. Or even if it was in some way documented.

    ;-)

  2. Scott says:

    The batch file doesnt work for me, has something do with %PATHTEXT% not being set, it seems.

  3. Nate says:

    It also doesn’t work if you specify an extension; ‘whereis xcopy’ works but ‘whereis xcopy.exe’ does not.

  4. Mike Dunn says:

    Works fine for me, but remember you have to leave off the extension on %1. So "whereis notepad.exe" won’t find anything, use "whereis notepad" instead.

  5. A regular viewer says:

    I know Eric Lippert reads this blog quite often, but a considerable amount of VBScript that lies in Corporate Machines is bloat. FOR/? in CMD.EXE is very much unsung.

  6. mschaef says:

    Ran fine for me. Turned out to be very timely too… I had to use it to figure out which jdk a colleague’s machine was running. (so I hit the site, downloaded the script and went with it…)

    Thanks, Raymond.

  7. David Heffernan says:

    So it’s no use for DLLs since they aren’t included in PATHEXT.

  8. JamesW says:

    Does cmd.exe not have an equivalent of ‘which’?

    james$ which mount_ntfs

    /sbin/mount_ntfs

    That just finds the executable that would run if you typed in its name – but that’s what I’d be looking for in PATH anyway.

    @Scott – It’s %PATHEXT% (only one ‘T’) and that ought to be something like:

    .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH

    so Raymond’s script will be looking for stuff that Windows will execute too. Works for me (I named it seek.bat):

    D:>seek msdev

    c:Program FilesMicrosoft Visual StudioCommonMSDev98BinMSDEV.COM

    c:Program FilesMicrosoft Visual StudioCommonMSDev98BinMSDEV.EXE

  9. fleeb says:

    To find ‘notepad.exe’ (or anything else that specific), you could create the following batch file:

    @echo %~$PATH:1

    If it cannot find the file, it’ll say ‘ECHO is on.’. Which, of course, is counter-intuitive. Otherwise, it’ll print the path to the file it would execute if you just tried to run it as a command (sort of like the unix ‘which’ command).

    I found myself using this sort of trick a few months ago when I needed a quick-n-easy way to see if the user had installed some file somewhere from within a batch file:

    call :mytest SpiffyApp.EXE

    if not defined MYEXECUTABLEPATH goto :needexecutable

    rem blah blah blah

    rem stuff that uses MYEXECUTABLEPATH goes

    rem in here.

    :needexecutable

    echo %0 may only be executed on machines with SpiffyApp installed.

    goto :end

    :mytest

    set MYEXECUTABLEPATH=%~$PATH:1

    goto :EOF

    :end

    endlocal

  10. Vince says:

    Except for not including the cwd, it works great for me… I suppose you could create a temp internal path variable consisting of the current directory plus PATH. That would cover the remaining base, yes?

  11. Pavel A. Kostromitinov says:

    Oh man, once again 4nt.exe (so good in other places) turns to be incompatible with original cmd.exe

  12. Wayne Steele says:

    Vince:

    Here’s one that looks in your local directory as well:

    @setlocal && @set P2=.;%PATH% && @for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if NOT "%%~$P2:i"=="" echo %%~$P2:i

  13. Chris Lundie says:

    Thanks Raymond! I’ll let the MSN desktop search folks know that they’re trying too hard. (Just kidding, or am I?)

  14. Herb says:

    Nearly identical to the one I’d puzzled out a few years back. If you want to eliminate the extension problem (allowing "whereis notepad" or "whereis notepad.exe") you can modify it to be:

    @for %%e in (%PATHEXT%) do @for %%i in (%~n1%%e) do @if NOT "%%~$PATH:i"=="" echo %%~$PATH:i

  15. lowercase josh says:

    This won’t work on old Win9x machines (that use command.com), since they don’t do the fun %~ stuff. Poor, pitiful command.com… :)

    There’s also been a native win32 port of which in unxutils for a while, although you must specify the extension for that guy.

    And if you want it to work when you add an extension, one way is to change (%1%%e) to (%~n1%%e), although that simply ignores it, along with any path you might give it.

    fleeb:

    @echo:%~$PATH:1

    Though this isn’t really appropriate to shorten Raymond’s version because it’d print too many unsightly blank lines.

  16. romanp says:

    Hmmm… I always use "dir /s" for this. works like a charm

  17. Wayne Steele says:

    The problem with Dir /s is it take forever to look through a bunch of directories you’re not even using. Also, if you’ve got fiver different versions of nmake.exe on your machine, this will tell you which one is being invoked from your current environment.

    BTW, the modified script I posted earlier seems to behave weirdly if you run it with everything on one line. This one works better:

    @setlocal

    @set P2=.;%PATH%

    @for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if NOT "%%~$P2:i"=="" echo %%~$P2:i

  18. Eric Lippert says:

    I think the point is not that the C# solution is the best way to solve this particular problem, but rather that it is a skeleton upon which can be built more utilities.

    How would you modify your 90 character batch file to print out all files on the path with sizes greater than one meg, for example?

    90 character batch files are great for simple tasks and throwaway code, but they’re hard to maintain, they’re hard to debug, and they’re hard to extend to any task that benefits from, say, variables that can contain numbers.

  19. Mr. Ed says:

    The real question is, why doesn’t the OS come with this command in the first place?

  20. Tim Farley says:

    //4nt.exe…turns to be incompatible

    Ah, but in 4NT you can do it this way:

    @echo %@search[%1]

    Handy translation table to be found here:

    http://jpsoft.com/help/batchparms.htm

  21. A says:

    Ah, but in 4NT you can do it this way:

    Or just use the built-in WHICH command.

  22. The real question is, why doesn’t the OS come with this command in the first place?

    Where.exe ships with Win2003.

  23. BlackTigerX says:

    but they’re hard to maintain, they’re hard to debug

    …you are kidding right?

  24. Eric TF Bat says:

    Fleeb – the answer to the "echo is on" problem is:

    @echo.%~$PATH:1

    I don’t know when they put it in (it wasn’t in DOS 3), but echo followed by a dot will treat the rest of its argument literally – including leading spaces. So that whereas

    echo hello

    will print hello at the start of the line, even tho there are many spaces after the "echo" in that line,

    echo. hello

    (with many spaces after the dot) will work as you expect.

    (I hope this comes out in the comment. Blog comment fields with no preview button irritate me, especially when they also do random conversions of their input.)

  25. J. Edward Sanchez says:

    In 4NT, echo %@search[%1] will search for *any* file on the PATH, but the following will find only executable commands (internal, external, aliases, etc.):

    which %1

    If you feed it a non-executable filename, it will display the assocated executable for the filename.

  26. Keith Moore says:

    Eric — Maybe something like this:

    @if (%_echo%)==() echo off

    setlocal ENABLEEXTENSIONS

    :Loop

    for /F "delims=; tokens=1*" %%i in ("%path%") do (set _cur=%%i&set path=%%j)

    for %%i in ("%_cur%*") do if %%~zi GEQ 1048576 echo %%i

    if not "%path%"=="" goto :Loop

    goto :EOF

    I know, I know — you make a very valid point. However, the task you mention (finding all files on the path greater than 1M in size) is *possible* with a batch file.

    Honestly, I’d prefer Perl for something like this…

  27. Paul says:

    That would be so much easier if you just used the Unix ‘find’ command – for example, to find all files on the path larger than 2048 bytes:

    for a in echo $PATH | sed -e 's/:/ /g' ; do find $a -type f -size +2k ; done

    I’ll freely admit there is some line noise in there, but it’s an awful lot clearer than the batch file horrors above. :-)

  28. Mark Hampton says:

    My peeve with all but 4NT’s which command is that they ignore the HKLMsoftwaremicrosoftwindowscurrentversionApp Paths default entry for applications.

  29. Anonymous says:

    Jibberish » Blog Archive » ‘which’ for Windows

  30. Ben says:

    Well just wait until we get a real shell w/ Longhorn.

  31. emmenjay says:

    This works like the Unix "which" program, rather than the "whereis" program.

    The "which" program searches your PATH, while "whereis" searches a hard-coded set of common directories.

  32. Muck says:

    Ben, you can have a real shell *right now*. http://www.cygwin.com/

  33. Mike Dunn says:

    4NT is Da Bomb(tm) and it’s my command prompt of choice. Little things like list (quick file viewer) select (operate on a bunch of files at once, picking them from a menu), and a better for loop make life so much nicer.

  34. Anonymous Coward says:

    Second the http://www.cygwin.com comment…I like to put the bin dir in my path and mix Windows and bash scripting. Doing "dir /s" seems to be way faster than the Cygwin "find" equivalent, but for grovelling through text, grep/awk/perl/python can’t be beat.

  35. Windows Services for Unix is available for free download.

  36. Remmargorp Laer A Ton says:

    If you are on adventurous mood, you can always write a C# code with some interop that sequentially scans the MFT when HDD is found, skipping some less important data (there must be some, how else MFT is so big). You would still need a minifilter to get notifications of changes, as I suspect the filesystemwatcher may not be entirely up for the task – or could it be? Then you just need to write some code that keeps certain percentage of the beginning of the file/path index in memory, should it take too much memory, one can store compressed stream of it and start reading it from the pos that one in the memory ends parallel to using lucene.net to fuzzy match the part of the index on memory to your query.

    Or is there a flaw somewhere in this design? The performance and fuzzy matching possibilities should be enough for you average 2 TB workstation like mine. Should do until we get the better one in LongHorn.

  37. Atif Aziz says:

    This is a cool example indeed, Raymond.

    For those looking to include DLLs in the search, this can be done by simply modifying the outermost FOR loop as follows:

    @for %%e in (%PATHEXT%;.dll) do …

    Actually, to benefit from precise and already present funtionality in the OS that searches for binaries, one can create a tiny executable that wraps the SearchPath API. This is what I have done in a tool called FindPath that can be found here:

    http://www.raboof.com/projects/findpath/findpath.aspx

    The SearchPath API, and therefore the FindPath tool, actually look in more locations than just the PATH. See the following MSDN documentation on SearchPath for details:

    http://msdn.microsoft.com/library/en-us/fileio/base/searchpath.asp

    FindPath also goes one step further and can extract and use assembly manifests together with the Activation Context API to precisely locate the right version of a SxS-enabled DLL. This is useful for libraries like Common Controls 6.0, where doing a simple search along the PATH for COMCTL32.DLL won’t yield the right results in Windows XP and 2003.

  38. The % pseudo-variable does the trick.

  39. PITADEV says:

    Which.bat — "whereis" for old Unix people who hack their Windows something fierce

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index