【问题标题】:How do I write a cmdlet function to allow input "file" or pipeline?如何编写 cmdlet 函数以允许输入“文件”或管道?
【发布时间】:2015-01-29 11:55:48
【问题描述】:

我正在尝试编写一个 cmdlet,以反映像 cat 这样的 UNIX 命令给我的内容(在没有文件的情况下从文件或标准输入中读取)。它需要考虑以下可能性:

cmdlet -file <inputFileName>
cmdlet -object <objectName>
<someObject> | cmdlet

在这种情况下,我将“文件”的定义扩展为包含任意对象。

它必须处理具有以下优先级的参数:

  • 如果某个对象已明确指定或在管道中可用,请使用该对象。
  • 否则,使用文件名。

我的参数配置为:

[CmdletBinding(DefaultParameterSetName = "path")]
param(
    [Parameter(ParameterSetName = "path",
        Mandatory = $false,
        Position = 0)]
        [string] $file,
    [Parameter(ParameterSetName = "object",
        Mandatory = $false,
        Position = 0,
        ValueFromPipeline = $true)]
        [Object] $object,
    [Parameter(Mandatory = $false)]
        [Int] $addrsize,
    [Parameter(Mandatory = $false)]
        [String] $title
)

(虽然我怀疑最后两个参数不相关,但我已将所有参数放入其中)。

所以,有两个参数集,一个带有一个保存文件名的字符串,另一个带有一个对象,允许在管道中传递对象。

在我的begin 块中,我有以下代码来读取文件内容:

[byte[]] $bytes = $null
if($file) {
    $bytes = [IO.File]::ReadAllBytes((Resolve-Path $file))
}

因此,如果您提供 -file 参数,则数组将加载文件内容。

process 块中,我检查-object 参数以查看它是否存在(作为显式参数或在管道中)。如果是,我用它来用对象覆盖字节数组:

if (Test-Path variable:\object) {
    Write-Output "processing object" ## DEBUG code
    if ($object -is [Byte]) {
        $bytes = $object
    } else {
        $inputString = [string] $object
        $bytes = [Text.Encoding]::Unicode.GetBytes($inputString)
    }
}

现在我完全明白了,如果我使用:

Write-Output "blah" | MyFunction -file myfile.txt

然后管道优先,文件被忽略。

但是,即使我预计没有 管道,这似乎也在发生:

MyFunction -file myfile.txt

结果是,当我使用上面的最后一条语句时,bytes 数组被设置为空,因此文件被忽略。

我怎样才能重组这段代码来做我想做的事?是否有其他方法可以判断管道是否为空,以便我丢弃文件内容?

【问题讨论】:

    标签: powershell parameters pipeline


    【解决方案1】:

    快速回答

    if (Test-Path variable:\object)
    

    即使没有传递参数也是如此,因此只要您有$object 作为参数的可能性,它总是正确的。

    修复它:

    你可以做一些事情。最简单的就是使用:

    if ($object)
    

    就像您在 begin 块中对 $file 所做的那样。

    由于您使用的是参数集,因此您还可以检测正在使用的参数集名称:

    if ($PSCmdlet.ParameterSetName -eq 'object')
    

    其他想法:

    由于您使用的是参数集,您应该将$file$object 都设为必填项。毕竟,您希望至少提供其中一个。它们仅在各自的参数集中是强制性的。查看结果的最佳方法(尤其是对于更复杂的参数集)是执行函数的定义,使其在当前范围内定义,然后查看帮助:

    Get-Help MyFunction
    

    这将准确地向您展示 powershell 如何解释您的参数集(每个参数集有多少以及哪些参数是强制性和可选的)。

    $file 应该(但不是必须)命名为 $Path 以符合 Powershell 的风格。您可以包含一个别名,以便通过添加一个或多个 Alias 属性来接受文件作为备用名称:

    [Alias('File')]
    

    通常在 process 块中你想使用这样的东西:

    process {
        foreach($obj in $object) {
            # code that processes each object ($obj) in the pipeline
        }
    }
    

    这样做的原因是行为会有所不同,具体取决于您是通过参数还是通过管道传入对象。如果您执行'file1','file2',file3' | MyFunction,则将为每个文件调用一次进程块。但是如果你调用MyFunction -object 'file1','file2','file3',那么进程块将被调用一次$object 将是一个数组。使用foreach 可以让您以相同的方式处理这两种情况,而无需任何条件。

    你对Write-Object "processing object"的使用大概应该是Write-Verbose或者Write-Debug,只有分别用-Verbose或者-Debug调用函数时才会显示。您也可以使用Write-Host 始终写入屏幕,但that is discouraged

    【讨论】:

    • 最后一点,这只是临时调试代码,但您肯定给了我很多东西要研究,我一回到办公室就会做这些。谢谢大家。
    • 很好的答案,我实施了您的所有建议,它完全按照我的意愿工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 2020-03-30
    相关资源
    最近更新 更多