我可以确定 PowerShell 函数是否作为管道的一部分运行吗?

发布于 2024-10-09 11:52:19 字数 697 浏览 0 评论 0原文

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) ,它会将数组的内容漂亮地打印到控制台。

有办法做到这一点吗?如果有更“惯用”的方式来实现这种事情,我也有兴趣知道。

Can a PowerShell function determine if it is being run as part of a pipeline? I have a function which populates an array with instances of FileInfo which I would like to "yield" to the pipeline if the function is being run this way or produce some pretty output if the function is being invoked by itself from the command line.

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
        }
    }
}

Basically, I'm trying to figure out how to implement INVOKED_IN_PIPELINE. If it is run in a pipeline (e.g. Do-Something | format-table fullname), I would simply yield the array, but if run directly (e.g. Do-Something), it would pretty-print the contents of the array to the console.

Is there a way to do this? If there is a more "idiomatic" way to achieve this kind of thing, I would also be interested to know.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

无声无音无过去 2024-10-16 11:52:19

此信息作为 $PSCmdlet.MyInspiration 的一部分提供。您可以使用以下 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

This information is available as part of $PSCmdlet.MyInvocation. Here is a cmdlet you can use to experiment with this. What it does it to write out the contents of that property once for any command execution and then passes on the object (so you can manipulate the objects some more with bigger pipelines). What you'll see is that there is a property called PipelineLength which is equal to 1 when you run this command by itself and increases for each item in the pipeline. Also notice PipelinePosition. It tells you what position this command is in the pipeline.

NOTE: $PSCmdlet is only available when you write an advanced function (e.g. have the [CmdletBinding()] attribute.

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 $_
    }
}

Here are some examples:

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
怪我入戏太深 2024-10-16 11:52:19

执行此操作的“惯用”方法是输出特定的对象类型,然后为该对象定义格式化数据。该对象可以是自定义(基于 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

The "idiomatic" way to do this is to output a specific object type and then define formatting data for that object. The object can be a custom (C#/VB-based object) or a named PSObject. The advantage of this approach is that you can just output these objects and if there is no further pipeline output formatting (ie use of a Format command) then you're defined default output formatting will get used. Otherwise, one of the Format commands can override that default formatting. Here's an example:

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

Notice how the default output is different the second time we sent $obj down the pipe. That's because it used the custom formatting instructions provided since there were no explicit formatting commands used.

BTW there is always a pipeline even for $obj because that is implicitly piped to Out-Default.

Here is the custom format definition that is defined in a .ps1xml file that I named MyCustomFormatData.xml:

<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>

For more examples of how to format custom objects look at the files output by this command:

Get-ChildItem $pshome\*.format.ps1xml
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文