Some observations about Powershell script blocks

A PowerShell script block is a code package.  Functionally  it is  probably equivalent to what would properly be considered a function in some other programming languages.

It has two methods of invocation that produce different return types.  

Invokereturnasis() – This returns the output generated as an object stream, and Powershell will coerce the result as it normally does .  If there are no objects returned, the result will be null.  If one object is returned it will be a scalar (single object).   If more than one object is returned, they will get put into an object array ([object[]]).

Invoke() –  This  method  will  return  a collection of the generated output objects. If there was no output generated, it will return an empty collection.  The coercion rules still apply, but since there will only be one object returned (the collection) the result will always be a collection.

You can run the script block by using the invoke operator  (&), the dot operator (.), or by calling one of the invocation methods directly.  The operators will use the invokereturnasis() method.  To use the invoke() method you must call it directly.

A script block always has two automatic variables associated with it – $input and $args.  $input is an arraylist enumerator, and the underlying arraylist is populated with incoming objects from the pipeline.  $args is populated with objects coming from the script block’s argument list.  These variables can only be populated from those sources.  You can run a script block in the local scope by using the dot operator, but it will not close over and use the $input and $args variables already present in the local scope.  You must explicitly provide the script block with pipeline input and an argument list to populate $input and $args that are used inside the script block.

The $_ automatic variable may also be associated with a script block. The script block has a property called isfilter that controls how the script block will handle pipeline input, and populate it’s $input variable. 

If this property is set to $false, the pipeline input will be accumulated into the underlying arraylist of $input, and script execution will begin after that. You can use the methods of the arralist enumerator to step through the items in the arraylist or go back to the beginning of the list and start over. This makes the entire pipeline data stream available to the script block at once.  It also causes the script block to operate in blocking mode when used inside a pipeline.  When it is running in this mode it will not recognize or use the $_ variable.

If isfilter is set to true, the script block operates in streaming mode. It will recognize and use the $_ variable and it will immediately process each object as it arrives from the pipeline.  You can also use $input, but incoming pipeline objects will not accumulate in the arraylist.  It will only contain the current pipeline object, so you cannot use the $input methods to any effect.

When you use the Function statement to create a named function and provide it with a single script block, that script block will have its isfilter property set to $false.  $input will be fully populated and it’s methods used effectively. Used within the pipeline, that function will operate like a blocking cmdlet.

If you use the Filter statement to create a named function and provide it with a single script block, that script block will have isfilter set to $true.  $input will only contain the current pipeline object  and  it will operate like a streaming cmdlet.

You can use both the Function and Filter statements to create a named function that has multiple script blocks using the Begin, Process and End keywords.  Using these keywords overrides whatever the implicit setting for isfilter would have been based on which statement was used.  A script block that has the Process keyword will have its isfilter set to $true.  If the function has both a Process and an End block, then $input in the End block will be empty.  The isfilter setting of the Process block prevented it from being populated with the incoming objects from the pipeline.

 

3 responses to “Some observations about Powershell script blocks

  1. Nicely done thank you!

  2. very nicely done..

  3. Pingback: Strings and Scriptblocks | Jason's PowerShell Blog

Leave a comment