Ваш собственный ответ является надежным , и хотя он обычно медленный из-за необходимости запуска процесса PowerShell, его можно сделать значительно быстрее за счет оптимизации команды PowerShell, используемой для определения вызывающей оболочки :
@echo off
setlocal
CALL :GETPARENT PARENT
IF /I "%PARENT%" == "powershell" GOTO :ISPOWERSHELL
IF /I "%PARENT%" == "pwsh" GOTO :ISPOWERSHELL
endlocal
echo Not running from Powershell
SET MyEnvVariable=MyValue
GOTO :EOF
:GETPARENT
SET "PSCMD=$ppid=$pid;while($i++ -lt 3 -and ($ppid=(Get-CimInstance Win32_Process -Filter ('ProcessID='+$ppid)).ParentProcessId)) {}; (Get-Process -EA Ignore -ID $ppid).Name"
for /f "tokens=*" %%i in ('powershell -noprofile -command "%PSCMD%"') do SET %1=%%i
GOTO :EOF
:ISPOWERSHELL
echo. >&2
echo ERROR: This batch file may not be run from a PowerShell prompt >&2
echo. >&2
exit /b 1
На моей машине это работает примерно в 3-4 раза быстрее (YMMV), но все равно занимает почти 1 секунду.
Обратите внимание, что я добавил проверкуимя процесса pwsh
, чтобы решение работало с PowerShell Core .
Гораздо более быстрая альтернатива - хотя и менее надежная :
Решение ниже опирается на следующее допущение , которое истинно при установке по умолчанию :
Только система средапеременная с именем PSModulePath
постоянно определена в реестре (не также для пользователя one).
Решение основано на обнаружении присутствия пути пользователя в PSModulePath
, который PowerShell автоматически добавляет, когдаart.
@echo off
echo %PSModulePath% | findstr %USERPROFILE% >NUL
IF %ERRORLEVEL% EQU 0 goto :ISPOWERSHELL
echo Not running from Powershell
SET MyEnvVariable=MyValue
GOTO :EOF
:ISPOWERSHELL
echo. >&2
echo ERROR: This batch file may not be run from a PowerShell prompt >&2
echo. >&2
exit /b 1
Альтернативный подход для запуска нового cmd.exe
окна консоли по требованию :
Основываясь на предыдущем подходе, следующий вариант просто повторно вызывает командный файл в новом cmd.exe
окне при обнаружении его запуска из PowerShell .
Это не только более удобно для пользователяЭто также уменьшает проблему решений, приведенных выше, и дает ложные срабатывания: при запуске из интерактивного cmd.exe
сеанса, который был запущен из PowerShell , вышеуказанные решения откажутся запускаться, даже если они должны, как PetSerAl указывает.
Хотя приведенное ниже решение также не обнаруживает этот случай как таковое, оно все же открывает полезное - хотя и новое - окно с установленными переменными среды.
@echo off
REM # Unless already being reinvoked via cmd.exe, see if the batch
REM # file is being run from PowerShell.
IF NOT %1.==_isNew. echo %PSModulePath% | findstr %USERPROFILE% >NUL
REM # If so, RE-INVOKE this batch file in a NEW cmd.exe console WINDOW.
IF NOT %1.==_isNew. IF %ERRORLEVEL% EQU 0 start "With Environment" "%~f0" _isNew & goto :EOF
echo Running from cmd.exe, setting environment variables...
REM # Set environment variables.
SET MyEnvVariable=MyValue
REM # If the batch file had to be reinvoked because it was run from PowerShell,
REM # but you want the user to retain the PowerShell experience,
REM # restart PowerShell now, after definining the env. variables.
IF %1.==_isNew. powershell.exe
GOTO :EOF
После установки всех переменных среды обратите внимание, что последний оператор IF
также повторно вызывает PowerShell, но в новом окне с тем же , исходя из предположения, что calПользователь предпочитает работать в PowerShell.
После этого новый сеанс PowerShell увидит вновь определенные переменные среды, хотя учтите, что вам потребуется два последовательных exit
вызова для закрытия окна.