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)
Comments are closed. |
This post would be better if the batch file actually worked. Or even if it was in some way documented.
;-)
The batch file doesnt work for me, has something do with %PATHTEXT% not being set, it seems.
It also doesn’t work if you specify an extension; ‘whereis xcopy’ works but ‘whereis xcopy.exe’ does not.
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.
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.
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.
So it’s no use for DLLs since they aren’t included in PATHEXT.
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
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
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?
Oh man, once again 4nt.exe (so good in other places) turns to be incompatible with original cmd.exe
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
Thanks Raymond! I’ll let the MSN desktop search folks know that they’re trying too hard. (Just kidding, or am I?)
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
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.
Hmmm… I always use "dir /s" for this. works like a charm
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
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.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/where.asp
Platform SDK : SDK Tools : WHERE
The real question is, why doesn’t the OS come with this command in the first place?
//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
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.)
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.
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…
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 ; doneI’ll freely admit there is some line noise in there, but it’s an awful lot clearer than the batch file horrors above. :-)
My peeve with all but 4NT’s which command is that they ignore the HKLMsoftwaremicrosoftwindowscurrentversionApp Paths default entry for applications.
Jibberish » Blog Archive » ‘which’ for Windows
Well just wait until we get a real shell w/ Longhorn.
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.
Ben, you can have a real shell *right now*. http://www.cygwin.com/
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.
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.
Windows Services for Unix is available for free download.
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.
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.
The % pseudo-variable does the trick.
Which.bat — "whereis" for old Unix people who hack their Windows something fierce