如何在保持环境不变的情况下在单独的窗口中启动另一个 PowerShell 会话?

发布于 2025-01-12 12:01:28 字数 402 浏览 1 评论 0原文

在使用 PowerShell 时,我倾向于通过键入“

Start-Process wt -Verb runas

当我这样做时,会出现一个新窗口”来快速切换到管理模式(遗憾的是,Windows 中没有 sudo)。然而,在新的会议中,环境是全新的。在跳转到新窗口时是否可以保留变量、别名、工作目录和所有其他类似内容?如果没有,那么,这是一个有效的答案。


举个例子,我正在寻找这种行为:

第一个窗口

C:\test> $x = 123
C:\test> Start-Process wt

新窗口

C:\test> $x
123

While working in PowerShell I tend to quickly switch to admin mode by typing

Start-Process wt -Verb runas

When I do so, a new window appears (sadly, no sudo in Windows). In that new session however, the environment is totally fresh. Is it possible to keep variables, aliases, working dir and all other stuff of similar matter while jumping to a new window? If not, then well, it's a valid answer.


To give some example, I am looking for this behavior:

First window

C:\test> $x = 123
C:\test> Start-Process wt

New window

C:\test> $x
123

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

岁月打碎记忆 2025-01-19 12:01:28

根据设计,提升会话 (-Verb RunAs)不会继承调用者的环境变量甚至显式尝试通过环境都会被 底层 .NET API[1]

另外,无论您是否使用 -Verb RunAsa 的状态 PowerShell 会话(别名、函数、仅 shell 变量等)在启动另一个 PowerShell 进程时永远不会被继承,例如与 启动进程


一种解决方法是根据调用者状态的值,通过在提升的会话中执行的命令显式地、选择性地重新创建感兴趣的状态,但这非常麻烦并且有局限性,如以下示例所示:

# Define a few things to copy to the elevated session.
$x1 = 123
$x2 = '3" of snow' # !! See the caveat re regular variables below.
$env:foo = 1
$env:foo2 = 2
Set-Alias bar Get-Date
function baz { "hello, world" }

# Note: The following only copies the definitions above.
#       You could try to copy ALL such definitions, by omitting a target name / pattern:
#         Get-ChildItem env:
#         Get-ChildItem function:
#         Get-ChildItem alias:
#       CAVEAT: This will NOT generally work with *regular variables*.
Start-Process -Verb RunAs powershell @"
-NoExit -Command Set-Location -LiteralPath \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
$(Get-Variable x? | ForEach-Object { "`${$($_.Name)} = $(if ($_.Value -is [string]) { "'{0}'" -f ($_.Value -replace "'", "''" -replace '"', '\"')  } else { $_.Value }); " })
$(Get-ChildItem env:foo* | ForEach-Object { "Set-Item \`"env:$($_.Name)\`" \`"$($_.Value -replace '"', '\"\"')\`"; " })
$(Get-ChildItem function:bar | ForEach-Object { "`$function:$($_.Name) = \`"$($_.Definition -replace '"', '\"\"')\`"; " })
$(Get-ChildItem alias:baz | ForEach-Object { "`$alias:$($_.Name) = \`"$($_.Definition)\`"; " })
"@

重要

  • 我省略了对 Windows 终端 (wt.exe) 的调用,因为这会创建另一个 /em> PowerShell 会话,这意味着只会为该会话保留以下定义:

    • 环境变量。
    • 当前位置(工作目录),如果其默认 shell 配置为使用父进程的工作目录。或者,更可预测的是,传递工作目录。显式使用 -d 选项:
      wt.exe -d \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
    • 如果这足够了,您可以删除保留别名、函数和常规变量的命令,将 -WindowStyle Hidden 添加到 Start-Process,删除 -NoExit 在参数列表中的 -Command 之前,并在底部添加 wt.exe 调用。

  • 保留其他类型的定义需要直接在提升的 powershell 会话中工作,该会话将始终使用常规 (conhost.exe)然而,控制台窗口。

一般来说,最好将在常规会话和提升会话中都可用的定义放置在 $PROFILE 文件

作为补充,请参阅此答案以获取方便的函数Enter-AdminPSSession,它允许您传递脚本块在提升的会话中执行,您可以将调用者状态中的值作为参数传递给该会话。

注意:

  • 以上使用Windows PowerShell CLI,powershell.exe。要使用 PowerShell (Core) 7+,请替换 pwsh.exe

  • 以上内容涵盖以通用方式保留当前文件系统位置、环境变量、别名和函数。

  • 警告:相比之下,保留常规变量仅限于字符串和数字 - 本质上是那些数据类型的实例,其字符串化表示当解释为源代码文字时,会被识别为此类。

    • 通过不平凡的额外努力,通过使用带有 CLI 的 -EncodedCommand-EncodedArguments 参数的 Base64 编码,支持更多数据类型是可能的,如 这个答案,但是可以用类型保真度表示的类型范围从根本上受到 PowerShell 的限制基于 XML 的序列化基础设施 - 请参阅此答案

[1] 此限制也出现在 PowerShell(核心)中7,其中 Start-Process 现在有一个 -Environment 参数,用于修改继承的环境,但是不能< /em> 与 结合-动词 RunAs。不幸的是,从 v7.5.0 开始,这种无效的参数组合会被悄悄地忽略,而不是报告错误(这就是类似的 .NET API 调用中发生的情况):请参阅 GitHub 问题 #20923

By design, elevated sessions (-Verb RunAs) do not inherit the caller's environment variables and even explicit attempts to pass an environment are categorically prevented by the underlying .NET API.[1]

Also, whether or not you use -Verb RunAs, the state of a PowerShell session (aliases, functions, shell-only variables, ...) is never inherited when you launch another PowerShell process, such as with Start-Process.


A workaround is to explicitly and selectively recreate the state of interest via commands executed in the elevated session, based on values from the caller's state, but that is quite cumbersome and has limitations, as the following example shows:

# Define a few things to copy to the elevated session.
$x1 = 123
$x2 = '3" of snow' # !! See the caveat re regular variables below.
$env:foo = 1
$env:foo2 = 2
Set-Alias bar Get-Date
function baz { "hello, world" }

# Note: The following only copies the definitions above.
#       You could try to copy ALL such definitions, by omitting a target name / pattern:
#         Get-ChildItem env:
#         Get-ChildItem function:
#         Get-ChildItem alias:
#       CAVEAT: This will NOT generally work with *regular variables*.
Start-Process -Verb RunAs powershell @"
-NoExit -Command Set-Location -LiteralPath \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
$(Get-Variable x? | ForEach-Object { "`${$($_.Name)} = $(if ($_.Value -is [string]) { "'{0}'" -f ($_.Value -replace "'", "''" -replace '"', '\"')  } else { $_.Value }); " })
$(Get-ChildItem env:foo* | ForEach-Object { "Set-Item \`"env:$($_.Name)\`" \`"$($_.Value -replace '"', '\"\"')\`"; " })
$(Get-ChildItem function:bar | ForEach-Object { "`$function:$($_.Name) = \`"$($_.Definition -replace '"', '\"\"')\`"; " })
$(Get-ChildItem alias:baz | ForEach-Object { "`$alias:$($_.Name) = \`"$($_.Definition)\`"; " })
"@

Important:

  • I've omitted the call to Windows Terminal (wt.exe), as that would create another PowerShell session, which means that only the following definitions would be preserved for that session:

    • Environment variables.
    • The current location (working directory), IF its default shell is configured to use the parent process' working directory. Alternatively, and more predictably, pass the working dir. explicitly with the -d option:
      wt.exe -d \"$((Get-Location -PSProvider FileSystem).ProviderPath)\"
    • If that is enough, you can remove the commands that preserve aliases, functions, and regular variables, add -WindowStyle Hidden to Start-Process, remove -NoExit before -Command in the argument list, and add a wt.exe call at the bottom.
  • Preserving the other types of definitions requires working directly in the elevated powershell session, which will invariably use a regular (conhost.exe) console window, however.

In general, it's best to place definitions that should be available in both regular and elevated sessions in your $PROFILE file.

Complementarily, see this answer for convenience function Enter-AdminPSSession, which allows you to pass a script block to execute in the elevated session, to which you can pass values from the caller's state as arguments.

Note:

  • The above uses the Windows PowerShell CLI, powershell.exe. To use PowerShell (Core) 7+ instead, substitute pwsh.exe.

  • The above covers preserving the current file-system location, environment variables, aliases, and functions in a generic fashion.

  • Caveat: By contrast, preserving regular variables is limited to strings and numbers - in essence, instances of those data types whose stringified representation is recognized as such when interpreted as a source-code literal.

    • With nontrivial additional effort, supporting more data types is possible, by using Base64 encoding with the CLI's -EncodedCommand and -EncodedArguments parameters as shown in this answer, but the range of types that can be represented with type fidelity is fundamentally limited by PowerShell's XML-based serialization infrastructure - see this answer.

[1] This limitation also surfaces in PowerShell (Core) 7, where Start-Process now has an -Environment parameter for modifying the inherited environment, which, however, can not be combined with -Verb RunAs. Unfortunately, as of v7.5.0, such an invalid parameter combination is quietly ignored instead of reporting an error (which is what happens in an analogous .NET API call): see GitHub issue #20923.

香草可樂 2025-01-19 12:01:28

您不能保留变量,创建新窗口后您将立即丢失它们,您能做的最好的事情是创建一个包含所有活动的脚本,然后将其保存在同一工作目录中。

当您打开一个新窗口时,只需调用您的脚本即可为您提供与其他窗口中相同的信息。

You can not keep variables, you will lose them immediately after the new window is created, the best you can do instead is to create a script containing all your activities then save it in the same working directory.

When you open a new window just call your script that will be able to give you the same information as in the other window.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文