Your own answer is robust 虽然由于需要运行 PowerShell 进程而通常很慢,但通过优化用于确定调用 shell 的 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 的系统 环境变量被永久定义在注册表中(不是用户特定的)。
该解决方案依赖于检测PSModulePath 中是否存在用户特定路径,PowerShell 在启动时会自动添加该路径。
@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 运行。
这不仅对用户更方便,还减轻了上述解决方案产生误报的问题:当从 从 PowerShell 启动的交互式 cmd.exe 会话运行时,上述正如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,但在相同新窗口中,基于调用用户更喜欢在 PowerShell 中工作的假设.
新的 PowerShell 会话将看到新定义的环境变量,但请注意,您需要 两个 连续调用 exit 来关闭窗口。