【问题标题】:Powershell setting EnvironmentVariable works in ISE but not ConsolePowershell 设置环境变量在 ISE 中有效,但在控制台中无效
【发布时间】:2020-02-19 20:53:49
【问题描述】:

我想了解一个奇怪的 Powershell 行为。

如果我设置一个永久环境变量并启动如下进程

[Environment]::SetEnvironmentVariable('FOO','BAR','User')
Start-Process notepad

这在 ISE 编辑器中按预期工作,如果我在控制台中一个接一个地键入它。但是,如果我从控制台将它作为 .\script.ps1 脚本运行,Start-Process 将忽略新的或更改的环境变量。在执行 Start-Process 之前,甚至环境变量也已正确设置。我通过添加 Sleep 并手动检查环境变量对话框对此进行了测试。如果脚本第二次运行,进程将按预期读取环境变量,因为它之前已经更改过。

为什么在这种情况下控制台的行为与 ISE 中的不同?

我已经尝试过这是否与加载到 ISE 而不是控制台中的特定程序集有关,但它没有接缝。 我也尝试以 STA 身份运行,但它也不起作用。

【问题讨论】:

  • 您是否尝试点源脚本文件powershell.exe . .\.script.ps1 或设置环境变量以使用Machine 目标位置?
  • @Theo 使用Machine 作为目标位置有效。但是,这需要我以管理员身份运行控制台,这并不总是最佳的。是否可以说应用程序在User 环境变量之前检查Machine!?但这有点超出了我的理解,这怎么会破坏剧本。我还测试了从 cmd 运行*.ps1 脚​​本(带有User 变量),但也没有运气。两者都没有使用点源(使用User 变量)。我做的另一个测试是使用 PS2EXE 包装器,UserMachine 都不起作用。我的脑子现在炸了。
  • setting 上定位User 还是Machine 没有区别;无论您是否以管理员身份 开始一个新流程都会有所不同 - 请参阅我的回答,尤其是脚注。

标签: powershell


【解决方案1】:

注意:此答案特定于 Windows,因为 System.Environment.SetEnvironmentVariable 仅支持修改 持久 环境变量定义(通过目标范围 User 和 @ 987654327@) 那里。然而,PowerShell 如何确定子进程环境的基本原理也适用于类 Unix 平台。

[Environment]::SetEnvironmentVariable() 的目标范围 System.EnvironmentVariableTarget 参数为 UserMachine 更新持久环境变量定义在注册表中 - 它也不会更新当前进程的内存中变量

相比之下,目标Process更新当前进程的变量非持久性
因此,[Environment]::SetEnvironmentVariable('FOO','BAR','Process') 等同于 $env:FOO = 'BAR'

Start-Process 默认使用当前进程的环境变量[1],因此看不到由针对UserMachine 范围在同一进程中[2]

Start-Process-UseNewEnvironment 参数原则上旨在做你想做的事:它意味着使用环境变量值启动新进程从注册表中读取 /em>,忽略调用进程的值 - 但是,从 PowerShell [Core] v7.0 开始,此功能已损坏 - 请参阅 this GitHub issue

解决方法当前进程中定义新变量:

# Update both the registry and the current process.
foreach ($targetScope in 'User', 'Process') {
  [Environment]::SetEnvironmentVariable('FOO', 'BAR', $targetScope)
}

# Start a new process with the new value in effect.
Start-Process -NoNewWindow -Wait powershell '-c \"`$env:FOO is: $env:FOO\"'

请注意 - 与 -UseNewEnvironment 应该 做的不同 - 这使得新进程也继承所有 process-only(内存中)环境变量/值。


[1] 进程在启动时被赋予一个环境变量块,通常是 父进程的块的副本(就像 PowerShell 本身默认所做的那样创建子进程时)。该启动块可能反映也可能不反映当时的注册表定义。当进程终止时,环境块的进程内修改会丢失,除非它们被显式持久化,例如使用[Environment]::SetEnvironmentVariable() 和目标范围UserMachine。正如所有修改持久定义的环境变量的程序应该做的那样,[Environment]::SetEnvironmentVariable() 广播 Windows 消息 WM_SETTINGCHANGE 作为更改通知,但很少有程序被设计为监听它,因此很少有程序更新它们的进程内环境变量来响应(这并不适用于所有程序)。

[2] 但是,如果您以管理员身份使用-Verb RunAs(仅限 Windows)使用当前用户的凭据启动新进程,则新进程看到新的/更新的定义,因为它使用当前进程的环境变量,而是从注册表中读取当前的定义。支持>

【讨论】:

  • 设置UserProcess 环境变量就可以了。再次感谢我对数字世界的理解。为了澄清,当前的 Powershell 进程在开始时将所有环境变量存储在内存中,并且从控制台启动的任何进程都将基于它们而不是更新的注册表。这就是为什么我能够手动检查环境对话框并查看更新的条目,但它不会影响应用程序,因为该进程是从控制台启动的,该控制台仍将旧的环境变量保存在内存中。对吗?
  • @secondplace,正确;请查看我刚刚添加到答案中的新脚注。
猜你喜欢
  • 2018-09-08
  • 1970-01-01
  • 2020-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-29
  • 1970-01-01
相关资源
最近更新 更多