304

On Linux, I can do:

$ FOO=BAR ./myscript

to call "myscript" with the environment variable FOO being set.

Is something similar possible in PowerShell, i.e. without having to first set the variable, call the command, and then unset the variable again?

To be more clear about my use case - I don't want to use this as part of a script. Rather, I have a third-party script whose behavior I can control using environment variables, but, in this case, not command line arguments. So being able to alternate between typing

$ OPTION=1 ./myscript

and

$ ./myscript

would just be very handy.

4
  • 79
    That's not usually a helpful question, @EBGreen. The fact that the capability is there in UNIX shells suggests that there is a use for it. Off the top of my head: controlling the username and email address git uses for commits. There is no command-line option for those - you have to set them either in ~/.gitconfig, .git/config in each repository, or envars. Of those options, envars are clearly easier to set on-the-fly (and conveniently override the values in the files). So if I want to change my author name for one "git commit" in powershell, how to do it? Commented Jun 28, 2015 at 20:44
  • 17
    Completely agree that asking why this is needed is pointless. It is as common as borscht when executing at the command line on Linux and those of us forced now to suffer with powershell (a syntactic nightmare if ever one existed) constantly have to search for answers to obvious techniques. Most of the time, they don;t even exist in powershell unless you count writing long scripts to do trivial things. Count me deeply frustrated with that shell ... Commented Nov 19, 2018 at 20:54
  • 7
    This feature is under discussion for PowerShell 6. Commented Nov 29, 2018 at 15:02
  • 2
    Thanks for the link, @FranklinYu, but at this point it would be a hopefully in a not-too-distant future version after v7.0. Commented Oct 3, 2019 at 18:46

11 Answers 11

169

Generally, it would be better to pass info to the script via a parameter rather than a global (environment) variable. But if that is what you need to do you can do it this way:

$env:FOO = 'BAR'; ./myscript

The environment variable $env:FOO can be deleted later like so:

Remove-Item Env:\FOO
Sign up to request clarification or add additional context in comments.

10 Comments

Just a thought: Couldn't you just spawn a new PowerShell process, handing the scriptblock into it via the -Command parameter? That way you don't have to clean up the environment afterwards, since that will be contained in the child process. Although I am talking to a PowerShell MVP so this probably doesn't work :-)
Keith, we have a push-environmentblock and pop-environmentblock in Pscx for exactly this scenario ;-)
Don't you need to set env:foo back to its old value (perhaps unset) instead of removing it?
as @Joey mentioned, if you want to do env vars cleanly, you could: & {$pre = $env:foo; $env:foo = 'bar'; ./myscript; if ($pre) {$env:foo = $pre} else {Remove-Item env:\foo}... some might say unwieldy, but will avoid side effects...
I downvoted this answer because it opens with an opinion which is based around not understanding the question. The reason for having this mechanism is precisely that environment variables (for child processes) are not, or at least do not need to be, global variables (for the parent shell).
|
93

Making a 'subshell' by invoking powershell with a script block allows you to scope the changes to the environment:

pwsh -Command { $env:MYVAR="myvalue"; .\path\to.exe }

4 Comments

This is the only answer that doesn't pollute global environment, even briefly, besides the Start-Job answer which is hard to work with.
Maybe you need to use powershell instead of pwsh.
Is pwsh some sort of alias? The command is not known in a current PowerShell.
@Andreas, pwsh is th executable name of PowerShell (Core) 7+. In the built-in Windows PowerShell, use powershell.exe. (The two CLIs are largely identical in terms of parameters.) If you want to call the same executable that underlies the current session, use & (Get-Process -Id $PID).Path { $env:MYVAR="myvalue"; .\path\to.exe }. You pay a performance penalty for launching another PowerShell instance.
57

2 easy ways to do it in a single line:

$env:FOO='BAR'; .\myscript; $env:FOO=$null
$env:FOO='BAR'; .\myscript; Remove-Item Env:\FOO

Just summarized information from other answers (thank you folks) which don't contain pure one-liners for some reason.

6 Comments

Does not work - the variable is not removed.
The 2nd vay works for me, windows 11
use $env:FOO=$null instead of $env:FOO=''
"Because an environment variable can't be an empty string, setting one to $null or an empty string removes it." according to learn.microsoft.com/en-us/powershell/module/…
@Dois I fixed the answer, however, as James Skemp quoted, setting $env:FOO to an empty string produces the same effect of a variable deletion.
|
33

I got motivated enough about this problem that I went ahead and wrote a script for it: with-env.ps1

Usage:

with-env.ps1 FOO=foo BAR=bar your command here

# Supports dot-env files as well
with-env.ps1 .\.env OTHER_ENV=env command here

On the other hand, if you install Gow you can use env.exe which might be a little more robust than the quick script I wrote above.

Usage:

env.exe FOO=foo BAR=bar your command here

# To use it with dot-env files
env.exe $(cat .env | grep.exe -v '^#') SOME_OTHER_ENV=val your command

2 Comments

It doesn't need to be Gow, but it works on git for windows too as it provides env.exe.
@PhaniRithvij Thanks for the hint! For those wondering where you can find it (if it isn't already in your Windows PATH): The default install location is C:\Program Files\Git\usr\bin\env.exe. If you picked a different install location, open "Git Bash" from Windows' start menu and run cygpath.exe -w "$(command -v env)".
11

To accomplish the equivalent of the Unix syntax, you not only have to set the environment variable, but you have to reset it to its former value after executing the command. I've accomplished this for common commands I use by adding functions similar to the following to my PowerShell profile.

function cmd_special()
{
  $orig_master = $env:app_master
  $env:app_master = 'http://host.example.com'
  mycmd $args
  $env:app_master = $orig_master
}

So mycmd is some executable that operates differently depending on the value of the environment variable app_master. By defining cmd_special, I can now execute cmd_special from the command line (including other parameters) with the app_master environment variable set... and it gets reset (or even unset) after execution of the command.

Presumably, you could also do this ad-hoc for a single invocation.

& { $orig_master = $env:appmaster; $env:app_master = 'http://host.example.com'; mycmd $args; $env:app_master = $orig_master }

It really should be easier than this, but apparently this isn't a use-case that's readily supported by PowerShell. Maybe a future version (or third-party function) will facilitate this use-case. It would be nice if PowerShell had a cmdlet that would do this, e.g.:

with-env app_master='http://host.example.com' mycmd

Perhaps a PowerShell guru can suggest how one might write such a cmdlet.

Comments

7

You could do this by running the script as a Job:

Start-Job -InitializationScript { $env:FOO = 'BAR' } -FilePath .\myscript.ps1 |
    Receive-Job -Wait -AutoRemoveJob

You could also pass arguments to the script, using the ArgumentList parameter of Start-Job:

$jobArgs = @{
    InitializationScript = { $env:FOO = 'BAR' } 
    FilePath             = '.\myscript.ps1'
    ArgumentList         = 'arg1', 'arg2' 
}
Start-Job @jobArgs | Receive-Job -Wait -AutoRemoveJob

Advantages and disadvantages

  • You don't have to reset the environment variable after the script finishes (which would require try / finally to do it correctly even in the presence of exceptions).
  • The environment variable will be really local to the launched script. It won't affect other, possibly launched in parallel, jobs.
  • The script will run in its own, somewhat isolated environment. This means that the launched script can't set variables of the main script, it has to write to the success stream (implicitly or by calling another command that already writes to the success stream) to communicate back to the main script. This could be an advantage or a disadvantage, depending on the use case.

1 Comment

Is it possible to do something similar with interactive commands, that require user's input?
1

Considering that CMD is the native CLI on the Windows kernel (and is still the automation interface for lots of tools), you may be executing your PowerShell script with powershell.exe from the CMD prompt or an interface that accepts CMD console statements.

If you are using the -File parameter to pass your script to powershell.exe, no other PowerShell code can be used to set an environment variable for the script to access, so instead you can set your environment variables in the CMD environment before calling powershell.exe:

> set foo=bar && powershell.exe -File .\script.ps1

A single & will also work, but will allow the command to continue if the set failed for some reason. (Is this even possible? I have no idea.)

Also, it may be safer to wrap "foo=bar" in quotes so that nothing following gets passed to set as the variable contents.

Comments

1

In my use case I needed to set an environment variable so I can use it within a Docker Compose script. within my Powershell Script I define the variable use a semicolon then call docker-compose on same line

$env:PLATFORM="linux/x86_64" ; docker-compose up -d --build

within docker compose I can now just use my ${PLATFORM} variable.

which looks like this

...
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    platform: ${PLATFORM}
...

Comments

1

This works on Powershell 5.1, which was the default for Windows 10 and 11 I believe. It will set the value temporarily and restore previous value or unset the value as appropriate. Other solutions weren't restoring the value or might set an empty value when before there was no value.

$priorValue = switch (test-path Env:/foo) { $true {$env:foo} $false {$null} } 

try
{
    $env:foo = "bar"
    # do your thing with the environment variable set
}
finally
{
    $env:foo = $priorValue
}

Comments

0

Introduced in PowerShell 7.4:

Start-Process -Environment @{ foo = ‘bar’ } app

https://learn.microsoft.com/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.4#example-9-overriding-an-environment-variable-for-a-process

Comments

-7

You can scope variables to functions and scripts.

$script:foo = "foo"
$foo
$function:functionVariable = "v"
$functionVariable

New-Variable also has a -scope parameter if you want to be formal and declare your variable using new-variable.

1 Comment

Hmmm... Don't think this answer applies here. PowerShell variables aren't environment variables. Based on the question, the asker isn't using environment variables as scratch space (like one would in CMD [if that were the case, this answer would apply]), but needs to manipulate the environment before invoking an external command and then restore the environment afterward (or something to that effect).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.