Functions


Functions are a construct common to most programming languages that provide the basic modularization programmers have used for decades. PowerShell allows you to create a function by declaring it as follows:

  1. Use the Function keyword.

  2. Provide the name of your function.

  3. Enclose the function's code within {curly braces}.

A very basic function might look like this:

 function myFunction {   write-host "Hello" } 

Functions are nearly identical to script blocks except for two differences:1) functions are explicitly declared using the Function keyword; and 2) functions have a name. Otherwise, functions are practically the same as a script block.

This particular function doesn't accept any input arguments, nor does it really return any kind of value. It simply displays "Hello" on the screen. In languages like VBScript this function might have been written as a Sub since functions typically return some value in VBScript. However, in PowerShell there is not a separate construct if a value isn't being returned. Instead, you simply have the function not return anything if you don't need it to.

Note that you can interactively declare functions without writing a script. Try typing the following into PowerShell at the prompt:

 function myFunction { write-host "Hello" } 

This is the same function as the first example, but it is declared all on one line. Because it was entered into the command prompt within PowerShell, this function becomes available globally. In other words, it lives within the global scope. We talked a good deal about scope in Chapter 5 and elsewhere. You may recall that scope applies to the availability of functions just as it applies to the availability of variables. With myFunction declared globally, any child scopes such as scripts will be able to call the function. However, if you declare a function within a script, then only that script and its child scopes will be able to "see" the function and use it.

Functions can also be nested:

 Function Outer1 {   Function Inner1 {     #code A here   }   Function Inner2 {     #code B here   }   #code C here } Function Outer2 {   Function Inner3 {     #code D here   }   Function Inner4 {     #code E here   }   #code F here } 

If this were included in a script, then the entire script would be able to call either Outer1 or Outer2. However, the script would not be able to directly call any of the Inner functions since those exist within the Outer functions' private scopes. Any code within Outer1 (code C) is be able to call Inner1 and Inner2. However, the code within Outer2 (code F) is not be able to access Inner1 and Inner2 because those two functions are contained within the private scope of Outer1.

Input Arguments

Functions have two ways of accepting input arguments. Here's the first:

 Function add2 {   [int]$args[0] + [int]$args[1] } 

This could be called like this:

 PS> Add2 10 20 30 

This output shows that the two input arguments, 10 and 20, were successfully added. Inside the function these arguments were accessed by using the special $args variable. The $args variable is an array in which each element in the array represents one argument passed to the function. Even though this is a fairly informal technique for accepting input arguments, it may be difficult to follow when you read the script months later. A more formal, easier-to-maintain way to work with input arguments looks like this:

 Function add2 ([int]$x, [int]$y) {   $x + $y } 

You would call this in exactly the same way. Up front it defines that two input arguments of the Integer type are required.

A third way to declare this function is as follows:

 Function add2 {   Param ([int]$x, [int]$y)   $x + $y } 

This is the same idea, however the parameters are defined in a special Param section that must be the first line of code in the function, instead of defining the arguments as a part of the function declaration itself.

Once again, you can call the function as follows:

 PS> Add2 10 20 30 

However, when you specifically define and name arguments using either of the above techniques, you can also call the function by naming the arguments as you pass them in:

 PS> Add2 -x 10 -y 20 30 

This passes the value 10 specifically to the $x argument, and 20 to the $y arguments. It does not make a difference in the math, but for more complex functions this technique provides more control and allows you to pass in arguments out of order, if necessary.

You can also declare a default value for an argument. In this case, if the function is called without a value for the argument, the function may be able to proceed using a default value:

 Function add2 {   Param ([int]$x = 10, [int]$y = 10)   $x + $y } 

Calling the function with no input arguments results in a value:

 PS> Add2 20 

Returning a Value

Returning a value from a function is fairly easy - whatever the function outputs is also its return. So really, all of the sample functions we've looked at so far have returned a value. For example:

 PS> $result = add2 10 10 PS> $result 20 

Here, our Add2 function was called with 10 and 10 as input arguments. The result of the function was stored in $result. Anything output from the function becomes part of its result, not just the last thing it outputs. For example:

Functiontest.ps1

image from book
 $a = "Hello" function foo ($b) {   $b.ToUpper()   $b.ToLower() } $x = foo $a $x 
image from book

The function foo outputs both the uppercase and lowercase versions of the input argument. $x is set equal to foo's output. The result of this is:

 PS C:\> test\functiontest HELLO Hello 

This shows that $x contains both pieces of information output by the function.

You can also use the Return keyword to explicitly return a value:

 $a = "Hello" function foo ($b) {   return $b.ToUpper()   $b.ToLower() } $x = foo $a $x 

There's a caveat, though: Once you use Return, the function exits immediately. So, in the above example, $b.ToLower() would never execute, because it comes after the Return.

If a function produces output and uses Return, the value on the Return line is simply appended to any other output. Consider this function:

 $a = "Hello" function foo ($b) {   $b.ToUpper()   Return $b.ToLower() } $x = foo $a $x 

The result is identical to the first version of this function:

 PS C:\> test\functiontest HELLO Hello 

The idea of having a function return anything output from that function is neat, but it can also be confusing. We recommend that you concatenate all of your intended output into a variable, and use the Return keyword to return that value from the function. That way your function is only returning data through one technique, and it's a technique that's easy to spot when you're reviewing the code later.

Piping to Functions

The examples you've seen so far in this chapter have demonstrated that you can call functions outright. However, you can also pipe the output of other commands into a function. When you do this, the piped-in data is stored in the special $input variable. If multiple objects are piped to a function, then the function is called only once. In this case all of the objects are placed into $input at the same time. Here's a very simple example of a function that simply outputs whatever's piped in:

 function foo {   $input } 

Piping this function generates same output for the Get-Process cmdlet as the Get-Process generates by itself:

 PS C:\> get-process | foo Handles  NPM(K)    PM(K)      WS(K)   VM(M)   CPU(s)     Id ProcessName -------  ------   -----       -----   -----   ------     -- -----------      37       3    1080        3624      31     0.05   2208 acrotray     104       5    1144        3384      32     0.02    492 alg      61       2     548        2136      19     0.52   1076 ati2evxx      90       3    1152        4664      31     0.72   3960 ati2evxx     228       7    6316        8688      65     0.64   3216 BTSTAC~1     182       5    4004        7420      56    98.94   2216 BTTray      55       3    2060        3044      31     0.16   1508 btwdins    1093       9    2020        3964      31    51.44    816 csrss     218       5    2896        7716      45     0.30   5836 dllhost     697      15   18696        7036     108   160.30    604 explorer 

However, consider this revised function:

 function foo { $input | get-member } 

The function is now piping its input to the Get-Member cmdlet, which changes the output as follows:

 PS C:\> get-process | foo    TypeName: System.Diagnostics.Process Name                        MemberType     Definition ----                        ----------     ---------- Handles                     AliasProperty  Handles = Handlecount Name                        AliasProperty  Name = ProcessName 

Even though this output is truncated, it shows that $input was recognized as an object of the System.Diagnostics.Process type.

Here's another example:

 function foo {   foreach ($i in $input) {     $i.ProcessName   } } 

Below is the partial output from this function when Get-Process is piped to it:

 PS C:\> get-process | foo acrotray alg ati2evxx ati2evxx BTSTAC~1 BTTray btwdins csrss dllhost 

The function takes the output of Get-Process into the $input variable. It then goes through each object in $input and displays just the ProcessName property of each.

Function Phases

Functions can include up to four special script blocks that execute during different phases of execution. When filters are discussed in the next section, you'll see this applies to filters also. These script blocks use special names to identify themselves:

  • Begin: This script block is executed only once when the function or filter is first called.

  • Process: If multiple objects are passed into the function or filter through the pipeline, this script block is executed once for each object.

  • End: This script block is executed after all pipeline objects have been dealt with by the Process script block.

Understanding how these script blocks work might take a little time. With a function, everything in the pipeline is normally lumped together into the $input variable. However, if the function contains a Process block, then $input is null. Instead, the Process block uses the special $_ variable to access the current pipeline object. Here's an example:

 function foo {   Begin {     "Running processes:"   }   Process {     $_.ProcessName   }   End {     "Complete"   } } 

This produces the following output when Get-Process is piped to it:

 PS C:\> get-process | foo Running processes: acrotray alg ati2evxx ati2evxx BTSTAC~1 BTTray btwdins csrss dllhost explorer firefox Groove hpqgalry hpqtra08 hpwuSchd2 HPZipm12 Complete 

The Begin block runs first. For each object in the pipeline, Process is executed once, with the current object being put into the $_ variable. End is executed when all objects have been processed. Note that when using any of these blocks no code can appear outside a block. Any code within the function that's not within a block will result in an error. For example:

 function foo {   "Starting function foo"   Begin {     "Running processes:"   }   Process {     $_.ProcessName   }   End {     "Complete"   } } 

Results in this:

 PS C:\> get-process | foo Starting function foo 'begin' is not recognized as a cmdlet, function, operable program, or script file. At C:\test\blocktest.ps1:3 char:8 +   Begin <<<< { Get-Process : Cannot evaluate parameter 'Name' because its argument is specified as a script block and there is no input. A script block cannot be evaluated without input. At C:\test\blocktest.ps1:6 char:10 +   Process <<<< { 'end' is not recognized as a cmdlet, function, operable program, or script file. At C:\test\blocktest.ps1:9 char:6 +   End <<<< { PS C:\> 

This occurs because code exists outside a script block when script blocks are in use.



Windows PowerShell. TFM
Internet Forensics
ISBN: 982131445
EAN: 2147483647
Year: 2004
Pages: 289

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net