Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
498 views
in Technique[技术] by (71.8m points)

windows - Where is program's path for PowerShell command?

Command 'git' is available from PowerShell, I want to know where is git installed on my PC, so I tried to execute such 'script'

PS> where git

but after execution I see nothing, only prompt for new command. Question: How can I find path for command in Windows 10?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The existing answers are helpful, but I think a more systematic discussion is helpful too.

tl;dr

where is PowerShell's built-in alias for the Where-Object cmdlet; to invoke the external where.exe program, use .exe explicitly:[1]

# Note the use of '.exe' to disambiguate the external 'where.exe' program
# from PowerShell's built-in 'where' alias (for 'Where-Object').
PS> where.exe git
C:Program FilesGitcmdgit.exe

where.exe, whose purpose is return the full path of an executable in the system's path (in one of the directories listed in the $env:PATH environment variable), is unrelated to cmd (the legacy command processor): it is an external executable that comes with Windows, and it can be invoked from any shell, and therefore also from PowerShell.
By contrast, cmd does have so-called internal commands that indeed can only be called from cmd, such as mklink; in fact, in cmd you can use where <name> to infer whether a given (functioning) command <name> is internal or not: if there's no output, the command is internal (or doesn't exist at all).

Alternatively, use the equivalent and more flexible PowerShell counterpart to where.exe, the Get-Command cmdlet; it returns System.Management.Automation.CommandInfo instances (or instances of derived classes), whose .Source property contains the full path for a command-info object representing an external executable:

PS> (Get-Command git).Source
C:Program FilesGitcmdgit.exe

Note:

  • where.exe finds only executable files, whereas Get-Command by default looks for all command types (aliases, functions, cmdlets, ...) - see next section.

  • Unlike Get-Command, where.exe also finds executables located in the current directory. Get-Command doesn't do that, because PowerShell by design, for security reasons, doesn't allow calling executables located in the current directory by name only - a path is required (e.g., .foo).


PowerShell has different types of commands, which - in the case of name conflicts - have a predefined order of precedence to determine what type should be the effective command.

That is, if a given command name matches two or more commands, it is their type that determines which command is actually invoked.

This precedence is documented in the conceptual about_Command_Precedence help topic; in short, here is the command precedence by type in descending order (highest precedence first):

  • aliases
  • function
  • cmdlets (loosely speaking: functions implemented as compiled binaries)
  • external executables, including *.ps1 script files - see bottom section

An easy way to see what command types exist for a given name is to add the -All switch when calling the Get-Command cmdlet, which lists the matching commands in descending order of precedence; that is, the command that will actually be executed via the given name is listed first.

PS> Get-Command -All where

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           where -> Where-Object
Application     where.exe                                          10.0.18... C:WINDOWSsystem32where.exe

The result shows that the built-in where alias for the Where-Object cmdlet (whose purpose is to filter pipeline input) is the effective command when you submit where, and not the desired where.exe executable.

Given that the where.exe executable file name has the .exe extension that can distinguish it from the where alias, it is simplest to invoke where.exe with the filename extension, as shown at the top.

In cases where this is not possible (e.g., on Unix-like platforms, where executables typically do not have a filename extension or if an alias shadows a function), you can use the -Type parameter to get the command of interest, and invoke it with &, the call operator:

# Invokes where.exe, as only it is of type 'Application' (external executable)
& (Get-Command -Type Application where) git

Should there be multiple external executables whose base file name is where, it is the one from the directory listed earliest in $env:PATH that will be executed - see next section.


Precedence among external executables and *.ps1 scripts:

Note:

  • One important difference between cmd and PowerShell is that PowerShell - by design, for security reasons - does not allow you to invoke an external executable or .ps1 script located in the current directory by name only; to do so, you must use a path, in the simplest case by prepending . (or ./); e.g., to invoke an executable foo located in the current directory, you must use ./foo ...

  • The precedence between *.ps1 scripts and other executables in effect differs by platform (Windows vs. Unix-like platforms), as detailed below.

  • The following discussion assumes that a given command name isn't shadowed by higher-precedence command types, such as aliases, and resolves to an external executable or *.ps1 script.

Precedence rules:

  • When a command name resolves to potentially multiple external executables or *.ps1 scripts via the directories listed in the $env:PATH environment variable, the executable / script located in the directory that is listed earliest is invoked.

  • If, in that earliest directory:

    • the given name exactly matches an executable file name (e.g., where.exe) or script (e.g., foo.ps1), there is no ambiguity, and that executable / script is invoked.

    • the given name doesn't include a filename extension (e.g., foo), multiple executables can match (via implied filename extensions), and the one to actually invoke is determined as follows:

      • On Windows:

        • PowerShell gives precedence to its own scripts, so if a .ps1 script is present, it is the effective command; note that .ps1 scripts are executed in-process, unlike external executables, which invariably run in a child process.

        • Otherwise, it is the executable file whose filename extension is listed earliest among the executable extensions in the $env:PATHEXT environment variable; e.g., foo.bat takes precedence over foo.vbs, because, .BAT is listed before .VBS.

      • On Unix-like platforms (Linux, macOS):

        • Unix-like platforms determine executability solely via permissions, not via filename extensions, and executables in the vast majority of cases have no filename extension (e.g., just git, not git.exe as on Windows).

        • From PowerShell's perspective, the only filename extension that matters with respect to executability on Unix is .ps1, because it is PowerShell itself that considers such files executable - irrespective of whether they are from the system's perspective.

        • Therefore, in PowerShell on Unix, .ps1 is the only implied filename extension that can be omitted on invocation; e.g., you can invoke a script file foo.ps1 as just foo (assuming it is in the system's path).

        • If you have an external executable whose file name doesn't have a filename extension - as is typical - and a .ps1 file with the same base name in the same directory, it is the external executable that takes precedence - the reason is that the extension-less name is an exact match for the extension-less executable file name.

          • E.g., if external executable foo and foo.ps1 are located in the same (earliest) directory, submitting foo invokes the external executable, not foo.ps1 - unlike on Windows.

Note:

  • The precedence rules among multiple executables in a given directory also apply when using an explicit path (without a filename extension); e.g., invoking ./foo decides the precedence among multiple executables in the current directory whose base name is foo as described above.

  • Placing .ps1 scripts in a directory listed in $env:PATH and invoking them by (base) name only isn't all that common, although it is worth considering as an alternative to putting potentially many functions in one's $PROFILE file.

    • Unfortunately, the UX is poor on Linux, where, due to its case-sensitive file system, you must specify the (base) file name case-exactly on invocation, whereas PowerShell command invocation is otherwise case-insensitive; e.g., if the actual file name is Get-Foo.ps1, only Get-Foo works for invocation, not get-foo.

[1] As for why a call such as where git - i.e. mistaken use of Where-Object -


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...