Page 1 of 1

which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 26 Mar 2019 12:54
by jfl
[Edited after each new release, to update the version history, and list the features in the latest version]

Announcing which.exe, a tool for searching which programs will run at the command.com, cmd.exe, or PowerShell prompts.
This which.exe program strives to implement the best of both Unix which and Windows where.exe, and a bit more!

Features:
  • Allows passing just the base name of a command, and uses the PATHEXT environment variable to infer possible extensions.
    (Like Windows' own where.exe, but unlike all Windows ports of Unix which.)
  • Uses Unicode internally, and outputs file pathnames correctly in any code page. (Provided that the console font has the necessary characters!)
    (Again, Windows ports of Unix which all fail at that if a pathname contains non-ASCII characters.)
  • Knows about command.com, cmd.exe, and PowerShell specific rules. For example, the latter searches for *.ps1 before trying extensions from PATHEXT.
    Also command.com and cmd.exe search in ".", whereas PowerShell does not. (Both where.exe and Windows ports of Unix which fail at that!)
  • Recognizes UWP applications execution links, and displays their target.
  • By default outputs only the first match. Use option -a to display all matching programs. (Like Unix which)
  • A -i option allows to efficiently detect cmd.exe doskey macros, and PowerShell aliases.
    Run 'which -?' to get help on how to configure both shells for this to work.
    (where.exe has no equivalent; Unix which has this option for Unix shells, but Windows ports don't support that in Windows.)
  • A unique -l option displays the program(s) date/time and size; Resolves symbolic links; etc.
  • A unique -v option displays verbose comments about why some seemingly obvious programs will NOT run.
    For example in Vista and later, with cmd.exe variable NoDefaultCurrentDirectoryInExePath set, cmd does not search in ".". In Linux, it'll report name case errors.
  • A unique ability to search for names with wildcards. This is useful when you don't quite remember the exact name of a program.
  • Avoids listing twice the same executable when a path appears multiple times in the PATH.
  • Last, but not least, a single .exe that runs in all versions of DOS and Windows, from DOS 3 16-bits to Windows 10 64-bits. Including Windows 95 and XP.

which.exe is available as part of the latest System Tools Library release: SysTools.zip
Note that the zip file contains multiple versions of which.exe: The one at the root, which runs in all MS OSs, as explained above; And several stripped down versions, that run just in DOS, or in particular versions of Windows respectively (WIN32=x86/WIN64=amd64/ARM=arm32/ARM64=arm64).
It is also possible to build it from source for Unix (Linux, MacOS, FreeBSD, etc). The Linux version is usable in the Windows 10 LinuX SubSystem, or in any distribution of Linux. Caution: Unix builds output an executable named 'Which', with a capital W. Do not rename it to 'which', to avoid overwriting the original Linux 'which' from your Linux distribution!


To display a help screen with a full list of options, run:

Code: Select all

which -?

Note about the option -i introduced in version 1.12, for listing cmd doskey aliases:
As discussed in this thread, capturing the doskey macros for the current cmd shell is tricky.
Piping the 'doskey.exe /macros' output to 'which.exe -i' will not work. It is necessary to use a doskey macro for which itself:

Code: Select all

doskey /macros which=^(help ^& doskey /macros^) ^| which.exe -i $*
And the best way to define doskey macros in an extensible way is to use the AutoRun.cmd script.
See this post on this forum for explanations on how to use AutoRun.cmd, and how it works.
For PowerShell, the problem is very similar, and it is necessary to define a which function invoking which.exe.
Run 'which -?' to get help on how to configure both shells for this to work.


The source code is available on GitHub in the SysToolsLib repository: which.c
Use the version in the zip file above to get the latest which.exe features documented here.

If you find any problem with this which.exe, please report it on GitHub in SysToolsLib issues.

Versions history:
  • 1.16 2020-12-16 Recognize pwsh.exe as PowerShell. Option -l also shows the executable size. Supports UWP App. Exec. Links.
  • 1.15 2020-04-20 Internal changes for MacOS support. Features otherwise unchanged.
  • 1.14 2019-09-26 Added the ability to search names with wildcards. Added a verbose msg about case-independent matches in Unix.
  • 1.13 2019-06-22 Avoid searching twice in the same directory. Added program properties, visible in the .exe properties box Details tab.
  • 1.12 2019-03-01 Added a -i option allows to efficiently detect cmd.exe doskey macros, and PowerShell aliases.
  • 1.11 2019-01-16 Added option -- to stop processing switches. Allows searching for names that begin with a -.
  • 1.10 2018-03-22 Added option -l, and changed -v to display comments about programs excluded.
  • ...
  • 1.0 1987 Initial version for MS-DOS only

Re: which.exe v1.12 - Now efficiently detecting doskey macros and PS aliases

Posted: 28 Mar 2019 11:58
by aGerman
Jean-François

That's a neat tool, I like it :)
I have a question as to how option -a is intended to work. If I run 'which -a cmd.exe' I was expecting that more than one file was found on my Win10 x64. Also if I run it in the WOW64 subsystem, it reports that cmd.exe was found in System32, which isn't wrong since I know that System32 is the virtualized SysWOW64 folder in this case. Is there a way to see the real path? Same maybe if a path gets redirected via junction?
jfl wrote:
26 Mar 2019 12:54
a single .exe that runs in all versions of DOS and Windows, from DOS 3 16-bits to Windows 10 64-bits. Including Windows 95 and XP.
What kind of black magic did you apply here :shock: Windows Defender didn't like that witchcraft. I had to restore it from quarantine several times :lol:

Steffen

Re: which.exe v1.12 - Now efficiently detecting doskey macros and PS aliases

Posted: 02 Apr 2019 09:44
by jfl
aGerman wrote:
28 Mar 2019 11:58
That's a neat tool, I like it :)
Thanks!
aGerman wrote:
28 Mar 2019 11:58
I have a question as to how option -a is intended to work. If I run 'which -a cmd.exe' I was expecting that more than one file was found on my Win10 x64. Also if I run it in the WOW64 subsystem, it reports that cmd.exe was found in System32, which isn't wrong since I know that System32 is the virtualized SysWOW64 folder in this case. Is there a way to see the real path? Same maybe if a path gets redirected via junction?
The -a option will not report all programs on your system, but all that are accessible in the PATH to the which.exe application you use.
The System32 directory is indeed a tricky case. The 32 and 64 bits versions of which.exe will indeed NOT see the same cmd.exe executable in the %windir%\System32 directory.
But in both cases, they have a single cmd.exe executable in their respective PATH. So I chose to report just that.
aGerman wrote:
28 Mar 2019 11:58
jfl wrote:
26 Mar 2019 12:54
a single .exe that runs in all versions of DOS and Windows, from DOS 3 16-bits to Windows 10 64-bits. Including Windows 95 and XP.
What kind of black magic did you apply here :shock: Windows Defender didn't like that witchcraft. I had to restore it from quarantine several times :lol:
There's no black magic :-). It's because the WIN32 version does not use the default DOS stub. (The one displaying "This program cannot run in DOS mode".)
Instead, it uses the DOS version of which.exe as the DOS stub for the WIN32 version. So the DOS version runs in MSDOS, and the WIN32 version runs in 32 and 64-bits versions of Windows.

If this triggers your Windows Defender, it's a false positive: The DOS version in the DOS stub does exactly the same as the Win32 version, but using int 21h calls!
I'm surprised this triggers your Windows Defender, because it does not trigger it on any of my systems.
But, on the other hand, the funny thing is that at work, the 64-bits debug version of which.exe (which I did not include in the zip file above) triggers the McAffee antivirus on one of my systems!?!
As a workaround, in the likely case that you have a 64-bits version of Windows, try using the WIN64 version of which.exe instead.

Re: which.exe v1.12 - Now efficiently detecting doskey macros and PS aliases

Posted: 02 Apr 2019 10:02
by aGerman
Thank you for your explanations!
jfl wrote:
02 Apr 2019 09:44
It's because the WIN32 version does not use the default DOS stub.
Yes that was already clear to me. My question was rather how you were able to mount the 32 bit machine code to the DOS code without screwing up the addresses/offsets. But I'm afraid the answer to this question is off topic in this forum.
jfl wrote:
02 Apr 2019 09:44
If this triggers your Windows Defender, it's a false positive
Oh I trust you. As I wrote, I restored the tool every time. It was just for letting you know.
My OS is Win10 Home x64 v. 10.0.17763.379 if that does help you somehow. And yes, the 64 bit version of WHICH has never been quarantined. Don't worry too much about that.

Steffen

Re: which.exe v1.12 - Now efficiently detecting doskey macros and PS aliases

Posted: 16 Apr 2019 06:20
by siberia-man
Yet another "batch only" implementation of the "which" utility can be found here: https://github.com/ildar-shaimordanov/c ... /which.bat

Code: Select all

:: USAGE:
::     which [-a] [--] name [...]
::
:: -a  Print all available matchings accordingly the description below.
::
:: For each of the names the script looks for and displays a doskey macro, 
:: the internal command information or the full path to the executable 
:: file in this order. The script doesn't mimic of the Unix command having 
:: the same name. It assumes specifics of the Windows command prompt. 
::
:: First of all, it looks for doskey macros because they have the higher 
:: priority in the prompt. The next step is a looking for internal 
:: commands from the known list of the commands. If the command is 
:: identified as internal the searching is stopped. 
::
:: If nothing has been found previously, the script continues searching of 
:: external commands in the current directory and the directories from the 
:: PATH environment. If no extension is specified, the PATHEXT variable is 
:: used for attempts to find the nearest filename corresponding the 
:: provided name. 
::
:: ENVIRONMENT:
::     PATH, PATHEXT
::
:: SEE ALSO:
::     DOSKEY /?
::     HELP /?
::     http://ss64.com/nt/
::
:: COPYRIGHTS
:: Copyright (c) 2010, 2014 Ildar Shaimordanov

@echo off

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 26 Sep 2019 05:45
by jfl
Here's version 1.14 of my which.exe program for DOS and Windows:
http://jf.larvoire.free.fr/progs/which_v1.14.zip
Note that this zip file contains multiple versions of which.exe: The one at the root, which runs in all Microsoft OSs, as explained in the opening post; And several stripped down versions, that run just in DOS, or in various versions of Windows (x86/amd64/arm64).
There's also a build for Linux. It is usable in the Windows 10/64 LinuX SubSystem, or in any x86_64 distribution of Linux. Caution : Do not to overwrite the original Linux which from your Linux distribution!

New in this which.exe version 1.14:
  • It can now search for names with wildcards. This is useful when you don't quite remember the exact name of a program.
    Example:

    Code: Select all

    C:\JFL\Temp>which -a *zip*
    C:\JFL\Tools\MakeZip.bat
    C:\JFL\Tools\UnxUtils\usr\local\wbin\bunzip2.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\bzip2.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\bzip2recover.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\gunzip.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\gzip.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\unzip.exe
    C:\JFL\Tools\UnxUtils\usr\local\wbin\zip.exe
    C:\JFL\Tools\GnuWin32\bin\bunzip2.exe
    C:\JFL\Tools\GnuWin32\bin\bzip2.exe
    C:\JFL\Tools\GnuWin32\bin\bzip2recover.exe
    C:\JFL\Tools\GnuWin32\bin\funzip.exe
    C:\JFL\Tools\GnuWin32\bin\gzip.exe
    C:\JFL\Tools\GnuWin32\bin\unzip.exe
    C:\JFL\Tools\GnuWin32\bin\unzipsfx.exe
    C:\JFL\Tools\GnuWin32\bin\zip.exe
    C:\JFL\Tools\GnuWin32\bin\zipcloak.exe
    C:\JFL\Tools\GnuWin32\bin\zipinfo.exe
    C:\JFL\Tools\GnuWin32\bin\zipnote.exe
    C:\JFL\Tools\GnuWin32\bin\zipsplit.exe
    
    C:\JFL\Temp>
  • The .exe property sheet details tab now reports lots of information about the program, and where it comes from.

    @aGerman: I hoped that this would prevent false positives with some anti-virus, but unfortunately this did not :(
    A few anti-virus still refuse to let it run :cry:
     
  • I've joined a Linux build. Obviously Linux does not need it, as it already has its own which program. This is more like a demonstration of the source portability.
    There's one useful thing though: The -v option will output a comment for every program that matches with the wrong case - A common error in Linux!
    Warning: Be cautious not to overwrite the original Linux which with this one: The original one is critical for lots of system management scripts, and mine probably isn't fully compatible.
    Recommendation: Rename it as Which (with a capital W), for example, to avoid overwriting the original Linux which.
     
  • I've joined an ARM64 build for Windows 10/arm64. I've never tested it as I don't have access to such a machine. If you do, please give it a try and tell me how it works!
     
  • Version 1.13 had a minor improvement: It avoided to report a program multiple times, when its path appears multiple times in a PATH. (A common problem in Windows, apparently)
Note that I failed to attach the version 1.14 zip file, as I did for version 1.12.
The upload says: "http error"
Maybe this is because it's larger, due to the other builds included. Is there an upload size limit?
Anyway, I've posted it on my ISP personal home page instead.

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 28 Sep 2019 04:37
by aGerman
jfl wrote:
26 Sep 2019 05:45
@aGerman: I hoped that this would prevent false positives with some anti-virus, but unfortunately this did not :(
A few anti-virus still refuse to let it run :cry:
That's normal. Unfortunately some established AVs still fire false positives. Also don't overdo. E.g. file version and product version usually contain the same data and they may expect to see a version string in both cases.
However, I still believe the combination of DOS and Windows 32 machine code makes the AV engines nervous.

Steffen

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 19 Dec 2020 05:32
by jfl
Hello,

I've released version 1.16 of my which.exe program for DOS and Windows.
(Version 1.15 had some internal changes for compatibility with MacOS, but the DOS/Windows versions were otherwise unchanged.)
It is available as part of the latest System Tools Library release: SysTools.zip

As usual, this zip file contains multiple versions of which.exe: The one at the root, which runs in all Microsoft OSs, as explained in the opening post; And several stripped down versions, that run just in DOS, or in various versions of Windows (x86/amd64/arm32/arm64).
Note that, for lack of an ARM64 system, I've never tested the arm64 version. But I did recently successfully test the arm32 version on a Raspberry Pi 2 running Windows 10 IoT. (An experiment worth another article here, as this IoT version has a severely limited command prompt!)

New in this which.exe version 1.16:
  • It now identifies pwsh.exe as PowerShell, and uses PowerShell search rules when invoked inside pwsh.exe. (pwsh.exe, also known as PowerShell Core, is the new cross-platform version of PowerShell.)
  • The -l option now displays the executable size, in addition to its time stamp.
  • It recognizes UWP applications execution links, and displays their target.
The latter is interesting, as it exposes a new twist in Windows 10, that complicates even further the search for which executable would run!

Universal Windows Platform (UWP) applications are new in Windows 10.
They are applications that can run on any type of Windows devices. (PCs, Phones, Xbox, IoT devices, etc)
Circa 2017, Windows 10 introduced a new kind of NTFS link, tagged as an IO_REPARSE_TAG_APPEXECLINK, or AppExecLink here for short.
Most UWP applications installations create an AppExecLink to the actual executable in "%LOCALAPPDATA%\Microsoft\WindowsApps".
This directory is in the Windows 10 PATH, and the links there avoid having to add each application's path to your global PATH.
(It's been a standard technique in Unix for ages to put symbolic links to applications in /usr/bin. A technique now adapted in a convoluted way in Windows - More on this below! Why make things simple when you can make them complicated? :-) )
These AppExecLinks differ from normal NTFS symbolic links in that they contain more than just a pointer to the executable. There are a class name, and the entry point name too.
I think the additional information helps Windows' CreateTask() speed up the UWP apps startup. (This is an educated guess, I've not been able to find any reliable information about that.)
Anyway, 3 years later, Windows 10 2020H2's cmd.exe and PowerShell 5.1 still report AppExecLinks as 0-bytes files.
Only the new PowerShell Core 7.0 correctly reports them as links.

Until now, the System Tools Library in general, and which.exe in particular, were not doing any better than cmd.exe.
But at last this new version released here handles them correctly.
For example if you have installed the new Windows Terminal, a UWP application itself, which.exe now finds it correctly, and my other tools truename.exe and dirc.exe do as well:

Code: Select all

C:\JFL\Temp>which wt
C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe

C:\JFL\Temp>which -v wt
# C:\Users\Larvoire\AppData\Local\Microsoft\WindowsApps\wt.exe # UWP App. Exec. Link
C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe

C:\JFL\Temp>which -l -v wt
# 2020-11-20 13:26:46        0 C:\Users\Larvoire\AppData\Local\Microsoft\WindowsApps\wt.exe -> C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe # UWP App. Exec. Link
2020-11-20 13:26:44    92672 C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe

C:\JFL\Temp>truename C:\Users\Larvoire\AppData\Local\Microsoft\WindowsApps\wt.exe
C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe

C:\JFL\Temp>dirc C:\Users\Larvoire\AppData\Local\Microsoft\WindowsApps\wt.exe

C:\Users\Larvoire\AppData\Local\Microsoft\WindowsApps

wt.exe -> C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.4.3243.0_x64__8wekyb3d8bbwe\wt.exe  <APPEXECL> 2020-11-20 13:26:46

1 files or directories listed.

C:\JFL\Temp>

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 19 Dec 2020 10:50
by aGerman
Jean-François

That's really cool. But alas, MS still makes things more opaque. If you try to find the target of MicrosoftEdge you'll get "C:\WINDOWS\system32\SystemUWPLauncher.exe". This is the correct target. However, simply running SystemUWPLauncher won't open Edge :lol: Nevermind, that's only a side note and actually has nothing to do with your WHICH tool.

Steffen

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 19 Dec 2020 15:32
by jfl
Maybe this is where the other information I was talking about are useful:
The Windows Terminal probably uses a standard entry point name, so that CreateProcess() can run wt.exe without the additional information;
Whereas Edge uses a non standard entry point?

You can see for yourself by dumping reparse points ( including UWP App. Exec. Links) by running:
fsutil reparsepoint query <REPARSE_POINT_PATHNAME>
Ex:

Code: Select all

C:\JFL\Temp>fsutil reparsepoint query "%LOCALAPPDATA%\Microsoft\WindowsApps\MicrosoftEdge.exe"
Reparse Tag Value : 0x8000001b
Tag value: Microsoft

Reparse Data Length: 0x110
Reparse Data:
0000:  03 00 00 00 4d 00 69 00  63 00 72 00 6f 00 73 00  ....M.i.c.r.o.s.
0010:  6f 00 66 00 74 00 2e 00  4d 00 69 00 63 00 72 00  o.f.t...M.i.c.r.
0020:  6f 00 73 00 6f 00 66 00  74 00 45 00 64 00 67 00  o.s.o.f.t.E.d.g.
0030:  65 00 5f 00 38 00 77 00  65 00 6b 00 79 00 62 00  e._.8.w.e.k.y.b.
0040:  33 00 64 00 38 00 62 00  62 00 77 00 65 00 00 00  3.d.8.b.b.w.e...
0050:  4d 00 69 00 63 00 72 00  6f 00 73 00 6f 00 66 00  M.i.c.r.o.s.o.f.
0060:  74 00 2e 00 4d 00 69 00  63 00 72 00 6f 00 73 00  t...M.i.c.r.o.s.
0070:  6f 00 66 00 74 00 45 00  64 00 67 00 65 00 5f 00  o.f.t.E.d.g.e._.
0080:  38 00 77 00 65 00 6b 00  79 00 62 00 33 00 64 00  8.w.e.k.y.b.3.d.
0090:  38 00 62 00 62 00 77 00  65 00 21 00 4d 00 69 00  8.b.b.w.e.!.M.i.
00a0:  63 00 72 00 6f 00 73 00  6f 00 66 00 74 00 45 00  c.r.o.s.o.f.t.E.
00b0:  64 00 67 00 65 00 00 00  43 00 3a 00 5c 00 57 00  d.g.e...C.:.\.W.
00c0:  49 00 4e 00 44 00 4f 00  57 00 53 00 5c 00 73 00  I.N.D.O.W.S.\.s.
00d0:  79 00 73 00 74 00 65 00  6d 00 33 00 32 00 5c 00  y.s.t.e.m.3.2.\.
00e0:  53 00 79 00 73 00 74 00  65 00 6d 00 55 00 57 00  S.y.s.t.e.m.U.W.
00f0:  50 00 4c 00 61 00 75 00  6e 00 63 00 68 00 65 00  P.L.a.u.n.c.h.e.
0100:  72 00 2e 00 65 00 78 00  65 00 00 00 31 00 00 00  r...e.x.e...1...

C:\JFL\Temp>

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 19 Dec 2020 16:13
by aGerman
Oh I didn't know that fsutil is able to do this for us. (I used DeviceIoControl() and dumped the data.)
However, SystemUWPLauncher is a launcher as the name indicates. So, my expectation was that we can pass an argument to open Edge. It's not the 1 that you find at the end of the dump. And it's also none of the other two strings "Microsoft.MicrosoftEdge_8wekyb3d8bbwe" or "Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge". Still I don't know how and why it works then.

Steffen

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 05 Jan 2021 11:58
by jfl
@Steffen

I've posted on https://stackoverflow.com/a/65583702/2215591 a reply with all what I've found so far on how to use App Exec Link targets.

I really hope that somebody eventually gets to the bottom of it, and explains how to use the information in these links (and just the information in these links) to start any UWP application.

When they do, I'll update which.exe to show the command-line that works.

If you do find something, please post it on the stackoverflow page above, as it's more focused on the App Exec Link subject.

Meanwhile, I'll probably revert which.exe to only show the link itself, and not its target: At least running the link itself is guarantied to work, even if it seems to be a 0-byte file.

I'll keep showing the target when using the (which -l) option though, as this might be interesting for the curious users.

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 05 Jan 2021 15:03
by aGerman
I bookmarked the topic. Thanks!
I've a question as to the ULONG Version; in your struct. Are there any references? All resources I found indicate that this is the number of strings in the list. However, since there are actually 4 strings in the list (if we count the ASCII digit at the end as string), I tend to agree that the meaning is rather something like a version number.

Steffen

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 06 Jan 2021 04:09
by jfl
I found this entry presented as a version there: https://www.tiraniddo.dev/2019/09/overv ... iases.html
And this made sense to me as, as you've noticed, there are 4 strings in the list, not 3.

Jean-François

Re: which.exe - The best of Windows where.exe and Unix which, and some more

Posted: 06 Jan 2021 11:50
by aGerman
Thanks Jean-François. After cross reading there also seems to be an explanation of how SystemUWPLauncher is involved. Worth digging deeper if I find some time ...

Steffen