PowerShell Cookbook

Twitter Updates

    follow me on Twitter

    Search

    Categories

     

    On this page

    Accepting Pipeline Input in PowerShell Scripts and Functions

    Archive

    Blogroll

    Disclaimer
    I work for Microsoft.

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    RSS 2.0 | Atom 1.0 | CDF

    Send mail to the author(s) E-mail

    Total Posts: 235
    This Year: 12
    This Month: 0
    This Week: 0
    Comments: 634

    Sign In

     Tuesday, August 15, 2006
    Tuesday, August 15, 2006 11:19:01 PM (Pacific Daylight Time, UTC-07:00) ( )

    This has been coming up a bunch in the last little while, and I realized that I haven’t had a very good resource to point people to when they ask how to make a script or a function deal with pipeline input.

    Scripts, functions, and script blocks all have access to the $input variable, which provides an enumerator over the elements in the incoming pipeline.  When pipelining is a core scenario, though,  these constructs also support the cmdlet-style statement blocks of begin, process, and end.  In those blocks, the $_ variable represents the current input object.  Along this line, a Filter (get-help about_Filter) is just a shorthand representation of a function whose body is composed entirely of a process block.

    The following script gives an example of using the cmdlet-style keywords in a script.  It is an update to a script I wrote nearly a year ago: a PowerShell Hex Formatter.

    ## format-hex.ps1
    ## Convert a byte array into a hexidecimal dump
    ##
    ## Example usage:
    ## get-content 'c:\windows\Coffee Bean.bmp' -encoding byte | format-hex | more

    ## Convert the input to an array of bytes.  This is a strongly-typed variable,
    ## so that we're not trying to iterate over strings, directory entries, etc.
    ## [byte[]] $bytes = $(foreach($byte in $input) { $byte })

    begin 
    {
        ## Store our header, and formatting information
        $counter = 0
        $header = "            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F"
        $nextLine = "{0}   " -f  [Convert]::ToString($counter, 16).ToUpper().PadLeft(8'0')
        $asciiEnd = ""

        ## Output the header
        "`r`n$header`r`n"
    }

    process
    {
        ## Display each byte, in 2-digit hexidecimal, and add that to the left-hand
        ## side.
        $nextLine += "{0:X2} " -f $_

        ## If the character is printable, add its ascii representation to
        ## the right-hand side.  Otherwise, add a dot to the right hand side.
        if(($_ -ge 0x20) -and ($_ -le 0xFE))
        {
           $asciiEnd += [char] $_
        }
        else
        {
           $asciiEnd += "."
        }

        $counter++;

        ## If we've hit the end of a line, combine the right half with the left half,
        ## and start a new line.
        if(($counter % 16) -eq 0)
        {
           "$nextLine $asciiEnd"
           $nextLine = "{0}   " -f [Convert]::ToString($counter, 16).ToUpper().PadLeft(8'0')
           $asciiEnd = "";
        }
    }

    end
    {
        ## At the end of the file, we might not have had the chance to output the end
        ## of the line yet.  Only do this if we didn't exit on the 16-byte boundary,
        ## though.
        if(($counter % 16) -ne 0)
        {
           while(($counter % 16) -ne 0)
           {
              $nextLine += "   "
              $asciiEnd += " "
              $counter++;
           }
           "$nextLine $asciiEnd"
        }

        ""
    }


    Keith Hill gives a good example of this technique as well in his Format-Xml function.