Accepting Pipeline Input in PowerShell Scripts and Functions
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 hexadecimal 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.