windows – 在CMD批处理文件中,我可以确定它是否是从powershell运行的吗?

前端之家收集整理的这篇文章主要介绍了windows – 在CMD批处理文件中,我可以确定它是否是从powershell运行的吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个 Windows批处理文件,其目的是设置一些环境变量,例如
=== MyFile.cmd ===
SET MyEnvVariable=MyValue

用户可以在完成需要环境变量的工作之前运行它,例如:

C:\> MyFile.cmd
C:\> echo "%MyEnvVariable%"    <-- outputs "MyValue"
C:\> ... do work that needs the environment variable

这大致相当于Visual Studio安装的“Developer命令提示符”快捷方式,它设置了运行VS实用程序所需的环境变量.

但是,如果用户碰巧打开了Powershell提示符,则环境变量当然不会传播回Powershell:

PS C:\> MyFile.cmd
PS C:\> Write-Output "${env:MyEnvVariable}"  # Outputs an empty string

对于在CMD和PowerShell之间切换的用户而言,这可能会造成混淆.

有没有办法可以在我的批处理文件MyFile.cmd中检测到它是从PowerShell调用的,所以我可以,例如,向用户显示警告?这需要在没有任何第三方实用程序的情况下完成.

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控制台窗口的替代方法

在以前的方法的基础上,以下变体只是在检测到它是从PowerShell运行时,在新的cmd.exe窗口中重新调用批处理文件.

这不仅对用户来说更方便,还可以缓解上述解决方案产生误报的问题:从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会话将看到新定义的环境变量,但请注意,您需要两次连续的退出调用才能关闭窗口.

猜你在找的Windows相关文章