Whats the cmd/powershell equivalent of `which` on bash?
The WHERE
command is not quite the same as unix which
because it lists all matching files found in the current directory or PATH. As Joey says, the first one listed is the one that what execute. It is simple to create a batch script that will only return the first one found.
@echo off
for /f "delims=" %%F in ('where %1') do (
echo %%F
exit /b
)
But WHERE
is relatively slow.
Below is a WHICH.BAT script that is faster and does a bit more. It uses extensive delayed expansion toggling because: 1) Expanding %PATH% is unreliable if there are unquoted special characters. 2) Expanding FOR variables while delayed expansion is enabled corrupts values that contain !
.
::WHICH.BAT CommandName [ReturnVar]
::
:: Determines the full path of the file that would execute if
:: CommandName were executed.
::
:: The result is stored in variable ReturnVar, or else it is
:: echoed to stdout if ReturnVar is not specified.
::
:: If no file is found, then an error message is echoed to stderr.
::
:: The ERRORLEVEL is set to one of the following values
:: 0 - Success: A matching file was found
:: 1 - CommandName is an internal command
:: 2 - No file was found and CommandName is not an internal command
:: 3 - Improper syntax - no CommandName specified
::
@echo off
setlocal disableDelayedExpansion
set "file=%~1"
setlocal enableDelayedExpansion
if not defined file (
>&2 echo Syntax error: No CommandName specified
exit /b 3
)
:: test for internal command
echo(!file!|findstr /i "[^abcdefghijklmnopqrstuvwxyz]" >nul || (
set "empty=!temp!\emptyFolder"
md "!empty!" 2>nul
del /q "!empty!\*" 2>nul >nul
setlocal
pushd "!empty!"
set path=
(call )
!file! /? >nul 2>nul
if not errorlevel 9009 (
>&2 echo "!file!" is an internal command
popd
exit /b 1
)
popd
endlocal
)
:: test for external command
set "noExt="
if "%~x1" neq "" if "!PATHEXT:%~x1=!" neq "!PATHEXT!" set noExt="";
set "modpath=.\;!PATH!"
@for %%E in (%noExt%%PATHEXT%) do @for %%F in ("!file!%%~E") do (
setlocal disableDelayedExpansion
if not "%%~$modpath:F"=="" if not exist "%%~$modpath:F\" (
endlocal & endlocal & endlocal
if "%~2"=="" (echo %%~$modpath:F) else set "%~2=%%~$modpath:F"
exit /b 0
)
endlocal
)
endlocal
>&2 echo "%~1" is not a valid command
exit /b 2
UPDATE
I had to significantly modify the script above because it was incorrectly listing an internal command as external if there happened to exist an exe file with the same root name somewhere in the PATH.
Various.
where
is a direct equivalent:C:\Users\Joey>where cmd C:\Windows\System32\cmd.exe
Note that in PowerShell
where
itself is an alias forWhere-Object
, thus you need to usewhere.exe
in PowerShell.In
cmd
you can also usefor
:C:\Users\Joey>for %x in (powershell.exe) do @echo %~$PATH:x C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
In PowerShell you have
Get-Command
and its aliasgcm
which does the same if you pass an argument (but also works for aliases, cmdlets and functions in PowerShell):PS C:\Users\Joey> Get-Command where CommandType Name Definition ----------- ---- ---------- Alias where Where-Object Application where.exe C:\Windows\system32\where.exe
The first returned command is the one that would be executed.