在新窗口中运行 PowerShell 自定义函数 [英] Run PowerShell custom function in new window

查看:37
本文介绍了在新窗口中运行 PowerShell 自定义函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Function Check-PC
{
$PC = Read-Host "PC Name"
If($PC -eq "exit"){EXIT}
Else{
Write-Host "Pinging $PC to confirm status..."
PING -n 1 $PC
}

这是我写到 PowerShell 脚本中的一个函数片段.我希望该函数在 PowerShell 的新实例中运行,而不是在主窗口中运行.

This is a snippet of a function I have written into a PowerShell script. I would like the function to run in a new instance of PowerShell, not in the main window.

是否可以在 PowerShell 的单独进程中运行它,而无需将其编写为单独的脚本并调用该脚本?像这样:

Is it possible to run this in a separate process of PowerShell without writing it as a separate script and calling the script? Something like this:

$x= Start-Process powershell | Check-PC

如果可能,我需要将所有内容都保存在一个脚本中.

I need to keep everything in one script if possible.

推荐答案

注意:Start-Process 的参与使解决方案显着复杂化 - 见下文.我f powershell直接从 PowerShell 调用的,你可以安全地传递一个脚本块,如下所示:

Note: It is the involvement of Start-Process that complicates the solution significantly - see below. If powershell were invoked directly from PowerShell, you could safely pass a script block as follows:

powershell ${function:Check-PC}  # !! Does NOT work with Start-Process

${function:Check-PC}变量命名空间符号的一个实例:它将函数的body 作为脚本块([scriptblock] 实例)返回;它是
Get-Content Function:Check-PC 的更简洁、更快速的等价物.

${function:Check-PC} is an instance of variable namespace notation: it returns the function's body as a script block ([scriptblock] instance); it is the more concise and faster equivalent of
Get-Content Function:Check-PC.

如果您需要将(仅限位置的)参数传递给脚本块,则必须附加 -Args,后跟作为 array 的参数(,-分隔).

If you needed to pass (positional-only) arguments to the script block, you'd have to append -Args, followed by the arguments as an array (,-separated).

请参阅相关问题的此答案的下半部分.

See the bottom half of this answer to a related question.

Start-Process powershell -args '-noprofile', '-noexit', '-EncodedCommand', `
  ([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes(
    (Get-Command -Type Function Check-PC).Definition)))

新的 powershell 实例不会看到您当前会话的定义(除非它们已在您的配置文件中定义),因此您必须传递函数的主体 到它(要执行的源代码).

The new powershell instance will not see your current session's definitions (unless they're defined in your profiles), so you must pass the body of your function to it (the source code to execute).

(Get-Command -Type Function Check-PC).Definition 将函数定义的主体作为字符串返回.

字符串需要转义,但是,为了传递给新的 Powershell 进程未修改.
" 嵌入在字符串中的实例将被剥离,除非它们表示为 \" 或三倍 (""").
(在这种情况下,需要 \" 而不是通常的 `" 来转义双引号,因为 PowerShell 在将命令传递给powershell.exe 可执行文件.)

The string needs escaping, however, in order to be passed to the new Powershell process unmodified.
" instances embedded in the string are stripped, unless they are either represented as \" or tripled (""").
(\" rather than the usual `" is needed to escape double quotes in this case, because PowerShell expects \" when passing a command to the powershell.exe executable.)

同样,如果函数体内的字符串作为一个整体或双引号字符串以(非空运行)\ 结尾,则 \ 将被解释为转义字符,因此\必须加倍.谢谢,PetSerAl.

Similarly, if the string as a whole or a double-quoted string inside the function body ends in (a non-empty run of) \, that \ would be interpreted as an escape character, so the \ must be doubled.Thanks, PetSerAl.

绕过这些引用(转义)问题的最可靠方法是将 Base64 编码的字符串与 -EncodedCommand 参数结合使用:

The most robust way to bypass these quoting (escaping) headaches is to use a Base64-encoded string in combination with the -EncodedCommand parameter:

  • [Convert]::ToBase64String()[byte[]] 数组创建一个 Base64 编码的字符串.

  • [Convert]::ToBase64String() creates a Base64-encoded string from a [byte[]] array.

[Text.Encoding]::Unicode.GetBytes() 转换(内部 UTF-16 -"Unicode") 字符串到 [byte[]] 数组.

[Text.Encoding]::Unicode.GetBytes() converts the (internally UTF-16 - "Unicode") string to a [byte[]] array.

如果您想传递完整函数,那么可以按名称调用它以传递参数,需要做更多的工作.

In case you want to pass the complete function, so it can be called by name in order to pass parameters, a little more work is needed.

# Simply wrapping the body in `function <name> { ... }` is enough.
$func = (Get-Command -Type Function Check-PC)
$wholeFuncDef = 'Function ' + $func.Name + " {`n" + $func.Definition + "`n}"

Start-Process powershell -args '-noprofile', '-noexit', '-EncodedCommand', `
  ([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("$wholeFuncDef; Check-PC")))

<小时>

Start-Process 使用基于正则表达式的源代码转义的解决方案:

PetSerAl 建议了以下替代方案,它使用正则表达式来执行转义.解决方案更简洁,但有点令人费解:


Start-Process solution with regex-based escaping of the source code to pass:

PetSerAl suggests the following alternative, which uses a regex to perform the escaping. The solution is more concise, but somewhat mind-bending:

Start-Process powershell -args '-noprofile', '-noexit', '-Command', `
  ('"' + 
   ((Get-Command -Type Function Check-PC).Definition -replace '"|\\(?=\\*("|$))', '\$&') +
   '"')

  • "|\\(?=\\*("|$)) 匹配每个 " 实例和 \<的每个非空运行/code> 字符. - 逐个字符 - 直接位于 " 字符之前.或字符串的末尾.

    • "|\\(?=\\*("|$)) matches every " instance and every nonempty run of \ chars. - character by character - that directly precedes a " char. or the very end of the string.

        在正则表达式的上下文中需要
      • \\ 来转义单个文字 \.
      • (?=\\*("|$)) 是一个肯定的前瞻断言,仅当后跟 " 时才匹配 \> 或字符串的结尾 ($),可以是直接的,也可以是中间有进一步的 \ 实例 (\\*).请注意,由于断言对匹配没有贡献,\ 字符,如果有多个字符,仍然一一匹配.
      • \\ is needed in the context of a regex to escape a single, literal \.
      • (?=\\*("|$)) is a positive look-ahead assertion that matches \ only if followed by " or the end of the string ($), either directly, or with further \ instances in between (\\*). Note that since assertions do not contribute to the match, the \ chars., if there are multiple ones, are still matched one by one.

      \$& 将每个匹配的字符替换为 \ 后跟字符本身 ($&) - 见我的这个答案,用于您可以在 -replace 表达式的替换字符串中使用的构造.

      \$& replaces each matched character with a \ followed by the character itself ($&) - see this answer of mine for the constructs you can use in the replacement string of a -replace expression.

      需要将结果包含在 "..." ('"' + ... + '"') 中以防止空格规范化;没有它,任何超过一个空格字符的运行.和/或制表符.将被规范化为单个空格,因为整个字符串不会被识别为 single 参数.

      Enclosing the result in "..." ('"' + ... + '"') is needed to prevent whitespace normalization; without it, any run of more than one space char. and/or tab char. would be normalized to a single space, because the entire string wouldn't be recognized as a single argument.

      • 请注意,如果您要调用 powershell 直接,PowerShell 通常会自动将字符串括在 "..." 中 在幕后,因为它在调用外部实用程序(本机命令行应用程序)时对包含空格的参数这样做,这就是 powershell.exe 是 - 与 Start-Process cmdlet 不同.
      • PetSerAl 指出,自动双引号机制并没有那么简单(字符串的具体内容与是否应用自动双引号有关),并且具体行为在 v5 中发生了变化,并且再次(稍微)在 v6 中.
      • Note that if you were to invoke powershell directly, PowerShell would generally automatically enclose the string in "..." behind the scenes, because it does so for arguments that contain whitespace when calling an external utility (a native command-line application), which is what powershell.exe is - unlike the Start-Process cmdlet.
      • PetSerAl points out that the automatic double-quoting mechanism is not quite that simple, however (the specific content of the string matters with respect to whether automatic double-quoting is applied), and that the specific behavior changed in v5, and again (slightly) in v6.

      这篇关于在新窗口中运行 PowerShell 自定义函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆