2

E.g. I want something like head that either accepts an array piped to it and then does select-object -first, or, receives a list of file names as parameters and outputs the first lines of each. So cat $filename | head should work like head $filename

Here's what I've tried so far:

Function head( )
{
    param (
      [switch] $help = $false,
      [parameter(mandatory=$false,ValueFromPipeline=$true)] [object[]] $inputs,
      [parameter(ValueFromRemainingArguments=$true)] [String[]] $files
    )

    If( $help )
    {
        Write-Host "usage: $($MyInvocation.MYCommand) [<file>] <numLines>"
        return
    }


    $lines = 0
    if ($files -and [int]::TryParse($files[0], [ref]$lines)) {
      $null,$files = $files
    } else {
      $lines = 10
    }

    $input | select-object -First $lines
    if ($files) {get-content -TotalCount $lines $files}
}

But this causes the function to ignore the first parameter:

C:\Users\idror.TLV-WPVAJ> head C:\temp\now.ps1
C:\Users\idror.TLV-WPVAJ> head C:\temp\now.ps1 C:\temp\now.ps1
Function head( )
{
    param (
          [switch] $help = $false,
          [parameter(mandatory=$false,ValueFromPipeline=$true)] [object[]] $input,
          [parameter(ValueFromRemainingArguments=$true)] [String[]] $files
        )

        If( $help )
        {
C:\Users\idror.TLV-WPVAJ> $a | head
1
2
3
C:\Users\idror.TLV-WPVAJ> $a | head 1
head : The input object cannot be bound to any parameters for the command either because the command does not take
pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:6
+ $a | head 1
+      ~~~~~~
    + CategoryInfo          : InvalidArgument: (1:Int32) [head], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,head
5
  • So the piped in array would be content or an array of file names? Commented Jul 25, 2016 at 14:38
  • the content. see update Commented Jul 25, 2016 at 14:40
  • Is your question about how to write the parameter sets or are you asking us to write the whole function for you? Have you tried anything so far? Commented Jul 25, 2016 at 14:45
  • i have tried and i'm getting all sorts of weird errors. Would like to see the whole function, but written so it works just for a very specific use case, but such that I can change the actual implementation (e.g. tail) Commented Jul 25, 2016 at 15:18
  • You should edit your question with the code you've tried and the specific errors you've gotten. We could maybe help with that. Commented Jul 25, 2016 at 15:21

1 Answer 1

1

You can achieve this using Parameter Sets:

function head {
[CmdletBinding()]
param(
    [Parameter(
        ParameterSetName = 'Content',
        ValueFromPipeline = $true,
        Mandatory = $true
    )]
    [String[]]
    $Content ,

    [Parameter(
        ParameterSetName = 'File',
        ValueFromRemainingArguments = $true ,
        Mandatory = $true
    )]
    [String[]]
    $Path,

    [Parameter()]
    [uint64]
    $Count = 5
)

    Begin {
        Write-Verbose "Parameter Set Name: $($PSCmdlet.ParameterSetName)"
    }

    Process {
        Write-Verbose "Content: $Content"
        Write-Verbose "Path: $Path"
        Write-Verbose "Count: $Count"
    }
}

First, take a look at the help output of this by running Get-Help head:

NAME
    head

SYNTAX
   head -Content <string[]> [-Count <uint64>]  [<CommonParameters>]

   head -Path <string[]> [-Count <uint64>]  [<CommonParameters>]

You can see that it interprets 2 different sets, and each parameter is mandatory in its own set.

You can also call it with -Verbose to see a demonstration of how this is working:

# Content by pipeline
'one','two','three' | head -Verbose

# Files as an array
head -Verbose 'file1','file2','file3'

# Files as remaining arguments
head -Verbose 'file1' 'file2' 'file3'
Sign up to request clarification or add additional context in comments.

2 Comments

nice. can i have a situation where I can have both? something like 1,2,3 | head -count 3 c:\temp\foo output 1,2,3,one,two,three (where one,two,three are the first lines in foo)?
@IttayD possibly, but it may be an ambiguous parameter set because pipeline binding isn't something that the engine uses to differentiate between parameters; in other words if you didn't call it via pipeline, it wouldn't know whether the single string you sent it was a file name or content (I think). You'd have to experiment with it. See my answer here for tips on troubleshooting parameter sets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.