A 90-byte “whereis” program


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. Anonymous says:

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

    ;-)

  2. Anonymous says:

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

  3. Anonymous says:

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

  4. Anonymous 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. Anonymous 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. Anonymous 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. Anonymous says:

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

  8. Anonymous 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. Anonymous 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. Anonymous 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. Anonymous says:

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

  12. Anonymous 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. Anonymous says:

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

  14. Anonymous 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. Anonymous 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. Anonymous says:

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

  17. Anonymous 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. Anonymous 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. Anonymous says:

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

  20. Anonymous 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. Anonymous says:

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

    Or just use the built-in WHICH command.

  22. Anonymous says:

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

    Where.exe ships with Win2003.

  23. Anonymous says:

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

    …you are kidding right?

  24. Anonymous 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. Anonymous 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. Anonymous 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. Anonymous 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. Anonymous 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. Anonymous says:

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

  31. Anonymous 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. Anonymous says:

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

  33. Anonymous 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 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. Anonymous says:

    Windows Services for Unix is available for free download.

  36. Anonymous 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. Anonymous 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. Anonymous says:

    The % pseudo-variable does the trick.

  39. Anonymous says:

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

Comments are closed.