I'm running Win10, with Cygwin, Git for Windows and Ubuntu WSL installed.
Running these commands in DOS and Powershell returns the following results:
# DOS
C:\>where grep
C:\cygwin64\bin\grep.exe
C:\Program Files\Git\usr\bin\grep.exe
# DOS
C:\>which grep
/usr/bin/grep
# Powershell
C:\> gcm grep*
CommandType Name Version Source
----------- ---- ------- ------
Application grep.exe 0.0.0.0 C:\cygwin64\bin\grep.exe
Application grep.exe 0.0.0.0 C:\Program Files\Git\usr\bin\grep.exe
Now when I run this, which one of the above actually runs?
# DOS / Powershell
C:\>grep ... ---> ?
Is it run in the order they appear here, or are listed in PATH environment variable? Or another order (which one)?
CodePudding user response:
Terminology aside: I assume you mean cmd.exe
aka the Command Prompt, the command shell of modern Windows versions, not the long-obsolete (MS-)DOS and its command.com
counterpart.
Is it run in the order they appear here, or are listed in PATH environment variable? Or another order (which one)?
Yes, the first executable whose path is listed in the results from your calls is the one that will be called if you submit grep
, i.e. a mere file name, as a command.
However, note that you should have called gcm grep
, not gcm grep*
, in which you'd have received only one result, because Get-Command
(whose built-in alias is gcm
), when given a literal name (rather than a wildcard expression), only returns the effective form / path of that command by default, as the Unix which
utility does.
Both Get-Command
and which
require opt-in if you want to know all forms / paths of a given name, namely via -All
and -a
, respectively, in which case they are listed in descending order of precedence, i.e. with the effective one printing first.
where.exe
, by contrast, invariably prints all paths, in descending order of precedence.
Among external programs - which are the only command form considered by which
and where.exe
- it is the order in which directories are listed in the PATH
environment variable that determines precedence: an executable with a given name is looked for sequentially in the directories listed, and the first one found is the effective one.
On Windows, if no file-name extension is given, it is the extensions listed in the PATHEXT
environment variable that are applied in sequence to look for a complete file name, so that grep
finds grep.exe
, for instance.
Get-Command
by default additionally looks for PowerShell-specific command forms, in the form of aliases, functions, cmdlets, as well as .ps1
script files, in that order, before considering external programs.
You can limit lookups to external programs with -Type Application
, but note that on invocation you may have to include the filename extension in order to bypass another command form that would otherwise take precedence.
For instance, where
would find / invoke the Where-Object
cmdlet (one of whose built-in aliases is where
), whereas where.exe
would find / invoke the external program.
On Unix-like platforms, where external programs typically don't have extensions, more work is needed on invocation; e.g., to invoke a hypothetical where
program without accidentally invoking the Where-Object
cmdlet, you'd have to use & (Get-Command -Type Application where)
Note that the same applies in principle to cmd.exe
and POSIX-compatible shells: they too have internal commands that can shadow external programs of the same name (e.g., dir
in cmd.exe
, and printf
in Bash); in cmd.exe
, you can again use the executable's file-name extension to ensure that the external program is targeted; in POSIX-compatible shells, you can call via $(which printf)
, for instance.
In short: Only Get-Command
in PowerShell gives you the full picture with respect to what a command a given name will actually invoke; the where.exe
and which
utilities - of necessity - are limited to external programs.
Finally, as Mofi notes, there is an important difference between cmd.exe
on the one hand and PowerShell and POSIX-compatible shells on the other:
Only
cmd.exe
allows you to execute an external program located in the current directory by name only.By security-minded design, PowerShell and POSIX-compatible shells do not support that, and require a path to refer to the program in this case; in the simplest form:
.\foo.exe
/./foo
.where.exe
does report executables in the current directory, so if you call it from PowerShell, you may get a false positive; that said, given the availability of the more flexibleGet-Command
cmdlet, there's no good reason to callwhere.exe
from PowerShell.