如何编写cmdlet函数以允许输入“文件”或管道?

时间:2015-01-28 08:54:40

标签: powershell parameters pipeline

我正在尝试编写一个cmdlet来镜像像cat这样的UNIX命令给我(在没有文件的情况下从文件或标准输入读取)。它需要允许以下可能性:

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

我正在扩展&#34;文件&#34;的定义。在这种情况下,包括任意对象。

必须按以下优先级处理参数:

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

我的参数配置为:

[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数组被设置为空,因此忽略该文件。

如何重构此代码以满足我的需求?还有另一种方法可以判断管道是否为空,以便我丢弃文件内容?

1 个答案:

答案 0 :(得分:2)

快速回答

if (Test-Path variable:\object)

即使未传递参数也是如此,只要您有$object作为参数可能性,它就会一直存在。

要修复它:

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

if ($object)

就像您在$file区块中使用begin一样。

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

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-VerboseWrite-Debug,只有在分别使用-Verbose-Debug调用该函数时才会显示。您也可以使用Write-Host始终写入屏幕,但that is discouraged