【问题标题】:Argument list and the pipeline参数列表和管道
【发布时间】:2013-09-27 07:28:55
【问题描述】:

作为此问题的补充:How do I force declared parameters to require explicit naming? 我正在努力解决管道问题。假设我想要 this 声明的行为:

param(
   $installdir, 
   $compilemode, 
   [Parameter(Position=0, ValueFromRemainingArguments=$true)] $files
   )

也就是说,我可以这样调用我的脚本:

c:\> MyScript -installdir c:\ file-1.txt file-2.txt file-3.txt

但现在我也想这样做:

c:\> gi file-*.txt |MyScript -installdir c:\

我可能会考虑像这样给参数添加一个装饰:

param(
   $installdir, 
   $compilemode, 
   [Parameter(
       Position=0, 
       ValueFromRemainingArguments=$true, 
       ValueFromPipeline=$true
   )] $files
   )

但实际发生的情况是我只在参数中加入了 1 个参数,即我没有得到包含 gi 生成的所有文件的数组,而是只得到列表中的第一个。

我尝试的第二种方法是使用$input 变量(而不是使用ValueFromPipeline 装饰器),但随后在尝试调用脚本时出现错误:

输入对象不能绑定到命令的任何参数 要么是因为该命令不接受管道输入或输入 并且它的属性与任何接受的参数都不匹配 管道输入。

我可以从这里去哪里?

【问题讨论】:

    标签: powershell parameters


    【解决方案1】:

    你可以在没有 ValueFromRemainingArguments 的情况下声明它:

    param(
       [Parameter(
           Position=0, 
           ValueFromPipeline=$true,
           ValueFromPipelineByPropertyName=$true)]
       [Alias('PSPath')]
       [string[]]
       $files,
       $installdir, 
       $compilemode
       )
    

    然后使用逗号运算符将多个文件作为数组传入,例如:

     MyScript -installdir c:\ file-1.txt,file-2.txt,file-3.txt
    

    注意:为了接受来自 Get-Item 和 Get-ChildItem 等命令的输入,请使用 ValueFromPipelineByPropertyName 并添加一个参数别名“PSPath”,它将在 Get-Item/Get-ChildItem 输出的对象上找到 PSPath 属性。

    我在 ISE 中对此进行了测试,效果很好:

    function foo
    {
        param(
           [Parameter(
               Position=0, 
               ValueFromPipeline=$true,
               ValueFromPipelineByPropertyName=$true)]
           [Alias('PSPath')]
           [string[]]
           $files,
           $installdir, 
           $compilemode
           )
    
        process {
            foreach ($file in $files) {
                "File is $file, installdir: $installdir, compilemode: $compilemode"
            }
        }
    }
    
    foo a,b,c -installdir c:\temp -compilemode x64
    ls $home -file | foo -installdir c:\bin -compilemode x86
    

    仅供参考,这是我一直用来创建接受管道输入或数组输入以及通配符路径的命令的模板:

    function Verb-PathLiteralPath
    {
        [CmdletBinding(DefaultParameterSetName="Path",
                       SupportsShouldProcess=$true)]
        #[OutputType([output_type_here])] # Uncomment this line and specify the output type  of this
                                          # function to enable Intellisense for its output.
        param(
            [Parameter(Mandatory=$true, 
                       Position=0, 
                       ParameterSetName="Path", 
                       ValueFromPipeline=$true, 
                       ValueFromPipelineByPropertyName=$true,
                       HelpMessage="Path to one or more locations.")]
            [ValidateNotNullOrEmpty()]
            [SupportsWildcards()]
            [string[]]
            $Path,
    
            [Alias("PSPath")]
            [Parameter(Mandatory=$true, 
                       Position=0, 
                       ParameterSetName="LiteralPath", 
                       ValueFromPipelineByPropertyName=$true,
                       HelpMessage="Literal path to one or more locations.")]
            [ValidateNotNullOrEmpty()]
            [string[]]
            $LiteralPath
        )
    
        Begin 
        { 
            Set-StrictMode -Version Latest
        }
    
        Process 
        {
            if ($psCmdlet.ParameterSetName -eq "Path")
            {
                if (!(Test-Path $Path)) {
                    $ex = new-object System.Management.Automation.ItemNotFoundException "Cannot find path '$Path' because it does not exist."
                    $category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
                    $errRecord = new-object System.Management.Automation.ErrorRecord $ex, "PathNotFound", $category, $Path
                    $psCmdlet.WriteError($errRecord)
                }
    
                # In the -Path (non-literal) case, resolve any wildcards in path
                $resolvedPaths = $Path | Resolve-Path | Convert-Path
            }
            else 
            {
                if (!(Test-Path $LiteralPath)) {
                    $ex = new-object System.Management.Automation.ItemNotFoundException "Cannot find path '$LiteralPath' because it does not exist."
                    $category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
                    $errRecord = new-object System.Management.Automation.ErrorRecord $ex, "PathNotFound", $category, $LiteralPath
                    $psCmdlet.WriteError($errRecord)
                }
    
                # Must be -LiteralPath
                $resolvedPaths = $LiteralPath | Convert-Path
            }
    
            foreach ($rpath in $resolvedPaths) 
            {
                if ($pscmdlet.ShouldProcess($rpath, "Operation"))
                {
                    # .. process rpath
                }
            }  
        }
    
        End
        {
        }
    }
    

    【讨论】:

    • 是的。我可能不得不这样做。我不喜欢它,因为本能说你用空格分隔列表。或者,也许我可以遍历 $args 变量,但它仍然给我留下了必须声明至少一个带有位置的参数的问题,以便其他参数按名称要求。叹息... perl 在这方面要好得多
    • 我想这里真正困扰我的是参数和管道对象之间似乎存在混淆。输入我脚本的东西还不是参数,当我尝试输入东西时我的 param() 声明失败,我不明白为什么......特别是当我声明一个 Position=0 的参数时它会抱怨
    • 在任何情况下,您的解决方案都不起作用,因为只要我声明一个带有 Position 值的参数,我似乎无法将任何内容通过管道传输到脚本中(我不明白为什么)
    • 获取文件数组的优势是大多数 PowerShell 用户在这一点上所期望的。这也意味着您可以使用相同的参数进行管道绑定。不知道为什么您会收到 Position 属性错误。为我工作。 :-)
    • 哦,我忘了说:我正在运行 Powershell 3.0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 2012-04-16
    • 2012-04-11
    • 1970-01-01
    相关资源
    最近更新 更多