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

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

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

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

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

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

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

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

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

Your own answer is robust虽然由于需要运行PowerShell进程而通常很慢,但通过优化用于确定调用shell的PowerShell命令,可以大大加快它:
  1. @echo off
  2. setlocal
  3. CALL :GETPARENT PARENT
  4. IF /I "%PARENT%" == "powershell" GOTO :ISPOWERSHELL
  5. IF /I "%PARENT%" == "pwsh" GOTO :ISPOWERSHELL
  6. endlocal
  7.  
  8. echo Not running from Powershell
  9. SET MyEnvVariable=MyValue
  10.  
  11. GOTO :EOF
  12.  
  13. :GETPARENT
  14. 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"
  15.  
  16. for /f "tokens=*" %%i in ('powershell -noprofile -command "%PSCMD%"') do SET %1=%%i
  17.  
  18. GOTO :EOF
  19.  
  20. :ISPOWERSHELL
  21. echo. >&2
  22. echo ERROR: This batch file may not be run from a PowerShell prompt >&2
  23. echo. >&2
  24. exit /b 1

在我的机器上,这个速度大约快3到4倍(YMMV) – 但仍然需要将近1秒.

请注意,我还添加了对进程名称pwsh的检查,以便使解决方案也适用于PowerShell Core.

更快的替代方案 – 虽然不那么强大:

下面的解决方案依赖于以下假设,在默认安装中也是如此:

只有名为PSModulePath的系统环境变量在注册表中持久定义(不是用户特定的).

解决方案依赖于检测PSModulePath中是否存在特定于用户的路径,PowerShell在启动时会自动添加该路径.

  1. @echo off
  2. echo %PSModulePath% | findstr %USERPROFILE% >NUL
  3. IF %ERRORLEVEL% EQU 0 goto :ISPOWERSHELL
  4.  
  5. echo Not running from Powershell
  6. SET MyEnvVariable=MyValue
  7.  
  8. GOTO :EOF
  9.  
  10. :ISPOWERSHELL
  11. echo. >&2
  12. echo ERROR: This batch file may not be run from a PowerShell prompt >&2
  13. echo. >&2
  14. exit /b 1

根据需要启动新cmd.exe控制台窗口的替代方法

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

这不仅对用户来说更方便,还可以缓解上述解决方案产生误报的问题:从PowerShell启动的交互式cmd.exe会话运行时,上述解决方案将拒绝运行,即使它们应该,正如PetSerAl指出的那样.
虽然下面的解决方案本身也没有检测到这种情况,但它仍然会打开一个可用的 – 尽管是新的 – 窗口,其中设置了环境变量.

  1. @echo off
  2. REM # Unless already being reinvoked via cmd.exe,see if the batch
  3. REM # file is being run from PowerShell.
  4. IF NOT %1.==_isNew. echo %PSModulePath% | findstr %USERPROFILE% >NUL
  5. REM # If so,RE-INVOKE this batch file in a NEW cmd.exe console WINDOW.
  6. IF NOT %1.==_isNew. IF %ERRORLEVEL% EQU 0 start "With Environment" "%~f0" _isNew & goto :EOF
  7.  
  8. echo Running from cmd.exe,setting environment variables...
  9.  
  10. REM # Set environment variables.
  11. SET MyEnvVariable=MyValue
  12.  
  13. 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.
  14. IF %1.==_isNew. powershell.exe
  15.  
  16. GOTO :EOF

在设置所有环境变量之后,请注意最后一个IF语句如何重新调用PowerShell,但是在同一个新窗口中,基于调用用户更喜欢在PowerShell中工作的假设.然后,新的PowerShell会话将看到新定义的环境变量,但请注意,您需要两次连续的退出调用才能关闭窗口.

猜你在找的Windows相关文章