PowerShell:仅为单个命令设置环境变量

在Linux上,我可以做:

$ FOO=BAR ./myscript

在设置环境变量FOO的情况下调用myscript。

在PowerShell中是否可能出现类似的情况,即无需首先设置变量,调用命令,然后再次取消设置变量?

为了更清楚地说明我的用例——我不想把它用作脚本的一部分。相反,我有一个第三方脚本,我可以使用环境变量控制其行为,但在本例中,不能使用命令行参数。所以能够在打字之间交替

$ OPTION=1 ./myscript

而且

$ ./myscript

会很方便的。

96294 次浏览

可以将变量作用于函数和脚本。

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

New-Variable也有一个-scope参数,如果你想要正式并使用New-Variable声明你的变量。

通常,通过参数传递信息给脚本比通过参数传递信息更好 全局(环境)变量。但如果这是你需要做的,你可以这样做:

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

环境变量$env:FOO可以稍后删除,如下所示:

Remove-Item Env:\FOO

要实现与Unix语法相同的功能,您不仅必须设置环境变量,还必须在执行命令后将其重置为以前的值。通过在PowerShell概要文件中添加类似于下面的函数,我已经为我使用的常见命令实现了这一点。

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

因此,mycmd是一些可执行文件,根据环境变量app_master的值,其操作方式不同。通过定义cmd_special,我现在可以通过app_master环境变量设置从命令行执行cmd_special(包括其他参数)…在执行命令后,它会被重置(甚至取消设置)。

据推测,您也可以为单个调用执行这种临时操作。

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

实际上应该比这个简单,但显然PowerShell并不支持这种用例。也许未来的版本(或第三方功能)将促进这个用例。如果PowerShell有一个cmdlet可以做到这一点,那就太好了,例如:

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

也许PowerShell专家可以建议如何编写这样的cmdlet。

我对这个问题有足够的动力,我继续为它写了一个脚本:with-env.ps1

用法:

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

另一方面,如果你安装了战争机器,你可以使用env.exe,它可能比我上面写的快速脚本更健壮一些。

用法:

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

考虑到CMD是Windows内核上的本机命令行(并且仍然是许多工具的自动化接口),您可能会从CMD提示符或接受CMD控制台语句的接口中使用powershell.exe执行PowerShell脚本。

如果你使用-File参数将你的脚本传递给powershell.exe,没有其他PowerShell代码可以用来设置脚本访问的环境变量,所以你可以在调用powershell.exe之前在CMD环境中设置你的环境变量:

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

单个&也可以工作,但如果set由于某种原因失败,则允许该命令继续执行。(这可能吗?我不知道。)

此外,将"foo=bar"用引号括起来可能更安全,这样之后的任何内容都不会作为变量内容传递给set

两种简单的方法:

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

只是从其他答案(谢谢大家)中总结了一些信息,出于某种原因,这些答案不包含纯一行程序。

你可以通过运行脚本作为工作来做到这一点:

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

你也可以使用Start-JobArgumentList形参向脚本传递参数:

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

优点和缺点

  • 你不需要在脚本完成后重置环境变量(即使出现异常,这也需要try / finally来正确执行)。
  • 环境变量实际上是启动脚本的本地变量。它不会影响其他可能同时推出的工作。
  • 脚本将在它自己的、有点孤立的环境中运行。这意味着启动的脚本不能设置主脚本的变量,它必须写入< em > < / em >成功流(隐式地或通过调用另一个已经写入成功流的命令)来与主脚本通信。这可能是一个优点,也可能是一个缺点,这取决于用例。

通过使用脚本块调用powershell来创建一个“subshell”,允许你对环境进行范围更改:

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

在我的用例中,我需要设置一个环境变量,这样我就可以在Docker撰写脚本中使用它。在我的Powershell脚本中,我使用分号定义变量,然后在同一行上调用docker-compose

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

在docker compose中,我现在可以使用我的${PLATFORM}变量。

就像这样

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

一个工具使用了一些我无法用pip获取的库。为了将路径保存在一个可记忆的位置,我在全局中设置了$env:PYTHONPATHWAIT。

使用结果是这样的

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