PowerShell函数能否确定它是否作为管道的一部分运行?我有一个函数用一个FileInfo
的实例填充一个数组,如果函数以这种方式运行我想要“屈服”到管道,或者如果函数是从它自己调用的话产生一些漂亮的输出。命令行。
function Do-Something {
$file_infos = @()
# Populate $file_infos with FileInfo instances...
if (INVOKED_IN_PIPELINE) {
return $file_infos
}
else {
foreach ($file_info in $file_infos) {
write-host -foregroundcolor yellow $file_info.fullname
}
}
}
基本上,我正在试图弄清楚如何实施INVOKED_IN_PIPELINE
。如果它在一个管道中运行(例如Do-Something | format-table fullname
),我只会产生数组,但如果直接运行(例如Do-Something
),它会将数组的内容漂亮地打印到控制台。
有办法做到这一点吗?如果有更“惯用”的方式来实现这种事情,我也有兴趣知道。
答案 0 :(得分:17)
此信息作为$ PSCmdlet.MyInvocation的一部分提供。这是一个cmdlet,您可以使用它来试验这个。如果为任何命令执行写出该属性的内容一次然后传递对象(这样你就可以用更大的管道来操纵对象)它是做什么的。您将看到的是,有一个名为PipelineLength
的属性,当您自行运行此命令时,该属性等于1,并且管道中的每个项目都会增加。另请注意PipelinePosition
。它告诉你这个命令在管道中的位置。
注意: $PSCmdlet
仅在您编写高级功能时可用(例如,具有[CmdletBinding()]
属性。
function test-PSCmdlet
{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
$test
)
Begin{
$once = $false
}
process
{
if (!$once)
{
write-host ($PSCmdlet.MyInvocation |out-string)
$once = $true
}
Write-Output $_
}
}
以下是一些例子:
PS C:\Users\jsnover.NTDEV> test-PSCmdlet
MyCommand : test-PSCmdlet
BoundParameters : {}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 14
HistoryId : 61
ScriptName :
Line : test-PSCmdlet
PositionMessage :
At line:1 char:14
+ test-PSCmdlet <<<<
InvocationName : test-PSCmdlet
PipelineLength : 1
PipelinePosition : 1
ExpectingInput : False
CommandOrigin : Runspace
PS C:\Users\jsnover.NTDEV> gps lsass | test-PSCmdlet |Format-table Name,Id -auto
MyCommand : test-PSCmdlet
BoundParameters : {[test, System.Diagnostics.Process (lsass)]}
UnboundArguments : {}
ScriptLineNumber : 1
OffsetInLine : 26
HistoryId : 62
ScriptName :
Line : gps lsass | test-PSCmdlet |Format-table Name,Id -auto
PositionMessage :
At line:1 char:26
+ gps lsass | test-PSCmdlet <<<< |Format-table Name,Id -aut
o
InvocationName : test-PSCmdlet
PipelineLength : 3
PipelinePosition : 2
ExpectingInput : True
CommandOrigin : Runspace
Name Id
---- --
lsass 620
答案 1 :(得分:6)
执行此操作的“惯用”方法是输出特定对象类型,然后为该对象定义格式数据。该对象可以是自定义(基于C#/ VB的对象)或命名的PSObject。这种方法的优点是您可以只输出这些对象,如果没有进一步的管道输出格式化(即使用Format
命令),那么您将使用定义的默认输出格式。否则,其中一个Format
命令可以覆盖该默认格式。这是一个例子:
PS> $obj = new-object psobject -Property @{FName = 'John'; LName = 'Doe'; `
BirthDate = [DateTime]"5/7/1965"}
PS> $obj.psobject.TypeNames.Insert(0, "MyNamespace.MyCustomTypeName")
PS> $obj
BirthDate FName LName
--------- ----- -----
5/7/1965 12:00:00 AM John Doe
PS> Update-FormatData .\MyCustomFormatData.ps1xml
PS> $obj
FName LName BirthDate
----- ----- ---------
John Doe 5/7/1965 12:00:00 AM
注意我们第二次向管道发送$obj
时默认输出是如何不同的。这是因为它使用了提供的自定义格式化指令,因为没有使用显式格式化命令。
顺便说一下,即使$obj
也存在管道,因为它隐含地传送到Out-Default
。
以下是我命名为MyCustomFormatData.xml的.ps1xml
文件中定义的自定义格式定义:
<Configuration>
<ViewDefinitions>
<View>
<Name>MyNamespace.MyCustomTypeName</Name>
<ViewSelectedBy>
<TypeName>MyNamespace.MyCustomTypeName</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Label>FName</Label>
<Width>25</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>LName</Label>
<Width>25</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>BirthDate</Label>
<Width>25</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>FName</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>LName</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>BirthDate</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
有关如何格式化自定义对象的更多示例,请查看此命令输出的文件:
Get-ChildItem $pshome\*.format.ps1xml