简体   繁体   中英

PowerShell: Setting an environment variable for a single command only

On Linux, I can do:

$ FOO=BAR ./myscript

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

Is something similar possible in PowerShell, ie 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.

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

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 easy ways to do it in a single line:

$env:FOO='BAR'; .\myscript; $env:FOO=''
$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.

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, eg:

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

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

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 will have to use Write-Output to communicate back to the main script. This could be an advantage or a disadvantage, depending on the use case.

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.

通过使用脚本块调用 powershell 来制作“子shell”允许您将更改范围限定为环境:

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

This was briefly mentioned in Peter Mortensen's answer , but a call operator is executed in a child scope; variables defined within them aren't adopted by the parent.

So, it would simply be:

& { $FOO='BAR'; CommandGoesHere }

Though, keep in mind if the command you execute is running a script, the script will also inherit the parent's PowerShell variables (if it's a PowerShell script) and environment variables.

For PowerShell 7+, if you want it to launch with your default environment without inheriting anything from the parent scope, you can launch it as a job like this with the background operator , though I don't think you get any output back until it's finished:

(. { Set-Variable 'FOO' 'BAR'; ./myscript.ps1 } &) | Receive-Job -Wait -AutoRemoveJob

#ALIASED VERSION#
(. { sv 'FOO' 'BAR'; ./myscript.ps1 } &) | rcjb -Wait -AutoRemoveJob

You have to use Set-Variable for this, because if there's a variable conflict with the parent scope, it throws an error where it parses it as $using:FOO ... probably a bug.

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}
...

A tool uses some libraries that I counld not optain with pip. To keep the path in a memoriable place I set $env:PYTHONPATHWAIT in global.

the usage end up like

$env:PYTHONPATH=$env:PYTHONPATHWAIT ; usdcat .\milkyway.usd

On Linux, I can do:

$ FOO=BAR ./myscript

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

Is something similar possible in PowerShell, ie 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM