如何编写 cmdlet 函数以允许输入 "file" 或管道?
How do I write a cmdlet function to allow input "file" or pipeline?
我正在尝试编写一个 cmdlet 来反映像 cat
给我的 UNIX 命令(从文件或没有文件的情况下的标准输入读取)。它需要允许以下可能性:
cmdlet -file <inputFileName>
cmdlet -object <objectName>
<someObject> | cmdlet
在这种情况下,我扩展了 "file" 的定义以包含任意对象。
它必须处理具有以下优先级的参数:
- 如果某个对象已明确指定或在管道中可用,请使用该对象。
- 否则,使用文件名。
我的参数配置为:
[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
数组设置为空,因此文件被忽略。
我怎样才能重构这段代码来实现我想要的?是否有另一种方法来判断管道是否为空,以便我 不 丢弃文件内容?
快速回答
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.
我正在尝试编写一个 cmdlet 来反映像 cat
给我的 UNIX 命令(从文件或没有文件的情况下的标准输入读取)。它需要允许以下可能性:
cmdlet -file <inputFileName>
cmdlet -object <objectName>
<someObject> | cmdlet
在这种情况下,我扩展了 "file" 的定义以包含任意对象。
它必须处理具有以下优先级的参数:
- 如果某个对象已明确指定或在管道中可用,请使用该对象。
- 否则,使用文件名。
我的参数配置为:
[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
数组设置为空,因此文件被忽略。
我怎样才能重构这段代码来实现我想要的?是否有另一种方法来判断管道是否为空,以便我 不 丢弃文件内容?
快速回答
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.