13

I have a batch script called SET_ENV.bat which contains environment variables that are used by other batch scripts. Currently this SET_ENV.bat is lauched by existing batch scripts.

Now I have a need to use Powershell script and I would like to launch the same SET_ENV.bat. I managed to do this using:

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

I know that the batch file was run because it contained an echo

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

But after looking at the environment variables, I can see that none of them have been updated. Is there something that is preventing me from updating environment variables with Powershell + batch file combo?

I have tried SET_ENV.bat directly from command line and it works. I have also tried Start-Process cmdlet with "-Verb runAs" but that didn't do any good.

masaz
  • 165
  • 1
  • 1
  • 7
  • I found someone talk about something similar in here: https://stackoverflow.com/questions/20077820/how-can-i-source-variables-from-a-bat-file-into-a-powershell-script but this did not provide answer/reason for why it does not work. – masaz Feb 28 '18 at 11:01
  • You can directly use PowerShell to do so. Something like this can work `[Environment]::SetEnvironmentVariable("TestVariableName", "My Value", " – Vivek Kumar Singh Feb 28 '18 at 11:05
  • 1
    @VivekKumarSingh: Or simply `$Env:TestVariableName = 'MyValue'`. No need to overcomplicate things. – Joey Feb 28 '18 at 11:06
  • @Joey: Yes that can be done too. I had seen a situation where the setting of scope became necessary. Hence, the over complicated code. :) – Vivek Kumar Singh Feb 28 '18 at 11:11

3 Answers3

9

Launching PowerShell again at the end of the batch commands will keep every environment variable so far.

My use case was: set up Anaconda environment, set up MSVC environment, continue with that. Problem is both Anaconda and MSCV have a separate batch script that initialises the env.

The following command starting from PowerShell will:

  • initialise Anaconda
  • initialise MSVC
  • re-launch 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'

Just swap the paths with what you need. Note that if the path contains spaces in needs to be inside double quotes ".

Breaking down the call above:

  • cmd.exe "/K": call cmd and do not exit after the commands finish executing /K

The rest is the full command, it is wrapped in single quotes '.

  • %USERPROFILE%\apps\anaconda3\Scripts\activate.bat %USERPROFILE%\apps\anaconda3: calls activate.bat with parameter ...\anaconda3
  • && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat": && and if the previous command didn't fail, run the MSVC vars setup file. This is wrapped in " as it has spaces in it.
  • && powershell: finally run PowerShell. This will now contain all environment variables from the ones above.

Just adding a better way of doing the aforementioned setup: using Anaconda's PowerShell init script to actually get it to display the environment name on the prompt. I won't break down this as it's just a modified command above.

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'' "'

Note that the single quotes in the powershell call are all doubled up to escape them

dtasev
  • 540
  • 6
  • 12
5

Environment variables are local to a process and get inherited (by default at least) to new child processes. In your case you launch a new instance of cmd, which inherits your PowerShell's environment variables, but has its own environment. The batch file then changes the environment of that cmd instance, which closes afterwards and you return back to your PowerShell script. Naturally, nothing in PowerShell's environment has changed.

It works in cmd since batch files are executed in the same process, so a batch file can set environment variables and subsequently they are available, since the batch file wasn't executed in a new process. If you use cmd /c setenv.cmd in an interactive cmd session you will find that your environment hasn't changed either.

You can try another option, such as specifying the environment variables in a language-agnostic file, to be read by either cmd or PowerShell to set the environment accordingly. Or you could launch your PowerShell scripts from cmd after first running your batch file. Or you could set those environment variables under your user account to no longer have to care for them. Or you just have one setenv.cmd and one setenv.ps1 and keep them updated in sync.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • Thank you for your comment! I thought that enviroment variables would be visible for any process, since I can already see some variables that I had set up last week. – masaz Feb 28 '18 at 11:08
  • 1
    There is a "base" set of environment variables you've configured system-wide and for your user account. Those are stored in the registry and Explorer updated its own environment whenever those change (via the settings UI at least). This then causes new processes (which are almost always started by Explorer) to pick up them since they get a copy of Explorer's environment. – Joey Feb 28 '18 at 11:11
0

Summary

Write the environment variables to file and load them after.

Example

I've included an MWE below that exemplifies this by saving and loading the VS-studio environment.

Usage

To run the script, call New-Environment. You will now be in the VS2022 environment.

How it works

The first time New-Environment is called, the VS-studio environment batch file runs, but the results are saved to disk. On returning to PowerShell the results are loaded from disk. Subsequent times just use the saved results without running the environment activator again (because it's slow). The New-Environment -refresh parameter may be used if you do want to resave the VS-studio environment again, for instance if anything has changed.

Script

NOTE: This script must be present in your powershell $profile so the second instance can access the function! Please ensure to change the VS path to reflect your own installation.

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)"
}
c z
  • 7,726
  • 3
  • 46
  • 59