【问题标题】:How to expand a PowerShell array when passing it to a function将PowerShell数组传递给函数时如何扩展它
【发布时间】:2010-10-01 17:24:14
【问题描述】:

我有两个 PowerShell 函数,第一个调用第二个。它们都采用 N 个参数,其中一个被定义为简单地添加一个标志并调用另一个。以下是示例定义:

function inner
{
  foreach( $arg in $args )
    {
      # do some stuff
    }
}

function outer
{
  inner --flag $args
}

用法如下所示:

inner foo bar baz

或者这个

outer wibble wobble wubble

目标是使后一个示例等效于

inner --flag wibble wobble wubble

问题:正如这里定义的那样,后者实际上导致两个参数被传递给inner:第一个是“--flag”,第二个是包含“wibble”的数组、“摇摆”和“摇摆”。我想要的是inner 接收四个参数:标志和三个原始参数。

所以我想知道的是如何说服 powershell 在将 $args 数组传递给inner 之前扩展它,将它作为 N 个元素而不是单个数组传递。我相信你可以在 Ruby 中使用 splatting 运算符(* 字符)做到这一点,我很确定 PowerShell 可以做到,但我不记得是如何做到的。

【问题讨论】:

    标签: arrays powershell


    【解决方案1】:

    PowerSHell V1 中没有很好的解决方案。在 V2 中,我们添加了 splatting(尽管出于各种原因,我们为此使用 @ 而不是 *)。这是它的样子:

    PS (STA-ISS) (2) > 函数 foo ($x,$y,$z) { "x:$x y:$y z:$z" }

    PS (STA-ISS) (3) > $a = 1,2,3

    PS (STA-ISS) (4) > foo $a # 作为单个 arg 传递

    x:1 2 3 y:z:

    PS (STA-ISS) (5) > foo @a # splatted

    x:1 y:2 z:3

    【讨论】:

    • 很荣幸能得到一位 PowerShell 专家的回答!我已经安装了 PowerShell 2 CTP2,所以我会试一试。
    • @Charlie @BrucePayette 我在 Powershell v5.1 下的& someexefile.exe $args 中有System.Object[]。当我执行 & someexefile.exe @args 时,它不适用于 Unicode 字符(chcp 65001 完成)。如何在 ps1 脚本之间传递 args?
    • @Artyom 嗯,好问题。它以什么方式不起作用?比如你看到了什么错误?
    • @Charlie 错误是传递的参数是“System.Object[]”而不是我的扁平化参数。
    【解决方案2】:

    嗯,可能有更好的方法,但看看这是否可行:

    inner --flag [string]::Join(" ", $args)
    

    【讨论】:

    • 看起来这不能直接工作,因为 string::Join 的结果是作为单个参数传递的,在这种情况下是“wibble wobble wubble”。
    • 嗯...让我再多看看
    【解决方案3】:

    基于@EBGreen 的想法,以及我在侧边栏中注意到的一个相关问题,一个可能的解决方案是:

    function outer
    {
        invoke-expression "inner --flag $($args -join ' ')"
    }
    

    注意:此示例使用 Powershell 2.0 CTP 的新 -join 运算符。

    但是,我仍然想找到一种更好的方法,因为这感觉像是一种黑客攻击,并且在安全方面非常糟糕。

    【讨论】:

    • 我也不喜欢调用表达式,但如果你控制了 args,它并不可怕。我认为 CTP2 或 CTP3 也暴露了标记器,在这种情况下,您可以在调用之前清理 args。
    • 您也可以仅使用 PowerShell v1.0 执行此操作:invoke-expression "inner --flag $args" 默认情况下,数组将使用空格连接(还有一种方法可以更改默认字符,但我不记得了)。
    • 是的,你说得对。分隔符由 $OFS 变量控制。
    【解决方案4】:

    如果你想要一个快速的现成解决方案,你可以复制粘贴我的:

    <#
        .SYNOPSIS
        Asks a question and waits for user's answer
    
        .EXAMPLE
        Usage with shortcuts and without ReturnValue
        Invoke-Question -Question "What would you like" -Answers "&Eggs", "&Toasts", "&Steak"
    
        Shows the quesiton and waits for input. Let's assume user input is 'S', the return value would be 2 (index of "&Steak")
    
        .EXAMPLE
        Usage without shortcuts and with ReturnValue
        Invoke-Question -Question "What would you like" -Answers "Eggs", "Toasts", "Steak" -ReturnValue
    
        Shows the quesiton and waits for input. The answers are prefixed with numbers 1, 2 and 3 as shortcuts.
        Let's assume user input is 2, the return value would be "Toasts" (prefixed numbers are "index + 1")
    
        .EXAMPLE
        Usage from pipeline with default value
        @("Eggs", "Toasts", "Steak") | Invoke-Question -Question "What would you like" -ReturnValue -Default 2
    
        Shows the quesiton and waits for input. The answers are taken from pipeline and prefixed with numbers 1, 2 and 3 as shortcuts.
        Steak is marked as default. If user simply continues without a choice, Steak is chosen for her.
        However, let's assume user input is 1, the return value would be "Eggs" (prefixed numbers are "index + 1")
    #>
    function Invoke-Question {
        [CmdletBinding()]
        param(
            # Main question text
            [Parameter(Mandatory = $true)]
            [string] $Question,
    
            # Question description, e.g. explanation or more information
            [Parameter(Mandatory = $false)]
            [string] $Description = "",
    
            # Default answer as index in the array, no answer is selected by default (value -1)
            [Parameter(Mandatory = $false)]
            [int] $Default = -1,
    
            # Set of answers, if the label is given with & sign, the prefixed letter is used as shortcut, e.g. "&Yes" -> Y,
            # otherwise the answer is prefixed with "index + 1" number as a shortcut
            [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
            [string[]] $Answers,
    
            # If set, returns a value of selected answer, otherwise returns its index in the Answer array
            [switch] $ReturnValue
        )
    
        begin {
            # init choices
            $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription]
            $answerNumber = 1
            $rememberAnswers = @()
        }
    
        process {
            #init answers
            foreach ($answer in $answers) {
                $rememberAnswers += $answer
                if ($answer -notmatch "&") {
                    # add number if shortcut not specified
                    $answer = "&$answerNumber $answer"
                }
    
                $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList $answer))
                $answerNumber++
            }
        }
    
        end {
            # ask question and return either value or index
            $index = $Host.UI.PromptForChoice($Question, $Description, $choices, $Default)
            if ($ReturnValue) {
                $rememberAnswers[$index]
            } else {
                $index
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      • 2020-11-01
      • 1970-01-01
      • 2018-04-26
      • 1970-01-01
      相关资源
      最近更新 更多