使用由PowerShell脚本启动的批处理文件设置环境变量

13

我有一个名为SET_ENV.bat的批处理脚本,其中包含其他批处理脚本使用的环境变量。目前,这个SET_ENV.bat是由现有的批处理脚本启动的。

现在我需要使用Powershell脚本,并且我想启动相同的SET_ENV.bat。我成功地使用以下命令进行了操作:

cmd.exe /c ..\..\SET_ENV.bat

我知道批处理文件被运行了,因为它包含了一个echo命令

echo *** Set the environment variables for the processes ***

但是在查看环境变量后,我发现它们中没有一个被更新。是否有什么东西阻止我使用Powershell +批处理文件组合更新环境变量?
我已经尝试过直接从命令行运行SET_ENV.bat并且它有效。我也尝试了带有“-Verb runAs”的Start-Process cmdlet,但这没有任何好处。

我在这里找到有人谈论类似的问题:https://dev59.com/9mIj5IYBdhLWcg3wmmNE 但是这并没有提供为什么它不起作用的答案或原因。 - masaz
您可以直接使用PowerShell来完成此操作。类似这样的代码可以实现:[Environment]::SetEnvironmentVariable("TestVariableName", "My Value", "<Option>") - Vivek Kumar Singh
1
@VivekKumarSingh:或者简单地使用$Env:TestVariableName = 'MyValue'。不需要过度复杂化事情。 - Joey
@Joey:是的,那也可以做到。我曾经遇到过需要设置范围的情况。于是就出现了过于复杂的代码。 :) - Vivek Kumar Singh
3个回答

9

在批处理命令结束时再次启动PowerShell将保留到目前为止的每个环境变量。

我的用例是:设置Anaconda环境,设置MSVC环境,并继续进行。问题是Anaconda和MSCV都有一个单独的批处理脚本来初始化环境。

PowerShell开始的以下命令将:

  • 初始化Anaconda
  • 初始化MSVC
  • 重新启动PowerShell
cmd.exe "/K" '%USERPROFILE%\apps\anaconda3\Scripts\activate.bat %USERPROFILE%\apps\anaconda3 && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && powershell'

只需将需要的路径替换即可。请注意,如果路径中包含空格,则需要将其放在双引号"内。
上面的调用分解如下:
- cmd.exe "/K":调用cmd,并在命令完成执行后不退出/K - 其余部分是完整的命令,使用单引号'括起来。
其中包含以下内容:
- %USERPROFILE%\apps\anaconda3\Scripts\activate.bat %USERPROFILE%\apps\anaconda3: 调用带有参数...\anaconda3activate.bat - && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat": && 如果前一个命令未失败,则运行MSVC变量设置文件。由于其中包含空格,因此将其用"括起来。 - && powershell: 最后运行PowerShell。这现在将包含上面所有环境变量。
只需添加一种更好的执行上述设置的方法:使用Anaconda的PowerShell初始化脚本实际显示环境名称。我不会详细说明,因为它只是上面的修改命令。
cmd.exe "/K" '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" && powershell -noexit -command "& ''~\apps\anaconda3\shell\condabin\conda-hook.ps1'' ; conda activate ''~\apps\anaconda3'' "'

请注意,powershell调用中的单引号都要加倍以进行转义。

5

环境变量是进程本地的,并且(默认情况下)会被新的子进程继承。在你的情况下,你启动了一个新的 cmd 实例,该实例继承了你的 PowerShell 的环境变量,但它有自己的环境。然后批处理文件更改了那个 cmd 实例的环境,之后关闭,你回到你的 PowerShell 脚本中。当然,PowerShell 环境中的任何内容都没有改变。

cmd 中工作是因为批处理文件在同一进程中执行,因此批处理文件可以设置环境变量,随后它们可用,因为批处理文件不是在新进程中执行的。如果你在交互式 cmd 会话中使用 cmd /c setenv.cmd,你会发现你的环境也没有改变。

你可以尝试另一种选项,例如指定语言无关文件中的环境变量,以由 cmd 或 PowerShell 读取并相应地设置环境。或者你可以在运行批处理文件之后从 cmd 启动你的 PowerShell 脚本。或者你可以在你的用户帐户下设置这些环境变量,以免再次关注它们。或者你只有一个 setenv.cmd 和一个 setenv.ps1,并保持它们更新同步。


非常感谢您的评论!我原本以为环境变量会对所有进程可见,因为我已经可以看到上周设置的一些变量了。 - masaz
1
您已经在系统范围和用户帐户上配置了“基本”环境变量集。它们存储在注册表中,当这些更改(至少通过设置UI)时,资源管理器会更新其环境。这会导致新进程(几乎总是由资源管理器启动)获取资源管理器环境的副本。 - Joey

0

总结

将环境变量写入文件并在之后加载。

示例

我在下面包含了一个 MWE,它通过保存和加载VS-studio环境来说明这一点。

用法

要运行脚本,请调用New-Environment。您现在将处于VS2022环境中。

工作原理

第一次调用New-Environment时,运行VS-studio环境批处理文件,但结果被保存到磁盘上。返回PowerShell时,从磁盘加载结果。后续使用已保存的结果而无需再次运行环境激活器(因为它很慢)。如果您确实想要重新保存VS-studio环境,则可以使用New-Environment -refresh参数,例如,如果有任何更改。

脚本

注意:此脚本必须存在于您的powershell $profile中,以便第二个实例可以访问该函数!请确保更改VS路径以反映您自己的安装。

function New-Environment()
{
    param (
        [switch]
        $refresh
    )
    Write-Host "Env vars now: $($(Get-ChildItem env: | measure-object).Count)"
    $fileName = "$home\my_vsenviron.json"

    if ((-not $refresh) -and (Test-Path $fileName -PathType Leaf))
    {
        Import-Environment($fileName)
        return;
    }

    $script = '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && '
    $script += "pwsh --command Export-Environment `"$fileName`""

    &"cmd.exe" "/C" $script

    Import-Environment($fileName)
}

function Export-Environment($fileName)
{
    Get-ChildItem env: | Select-Object name,value | ConvertTo-Json | Out-File $fileName
    Write-Host "I have exported the environment to $fileName"
}

function Import-Environment($fileName)
{
    Get-Content $fileName | ConvertFrom-json | ForEach-Object -process {Set-Item "env:$($_.Name)" "$($_.Value)"}
    Write-Host "I have imported the environment from $fileName"
    Write-Host "Env vars now: $($(Get-ChildItem env: | measure-object).Count)"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接