PowerShell Functions and Filters – The basics

PowerShell Tutorial 10: Functions and Filters


Let’s start off be defining the terms:

  • Function – A function allows you to name a block of code. The code block can then be called (by its name) once or multiple times anywhere from within your script.
  • Filter - A Filter (as it sounds) takes large amounts of pipeline data and displays only the results of interest to you.

PowerShell Functions

The main reason I use functions is script organization. It allows me the ability to call script blocks multiple times within a script, cutting down on the amount of code writing to accomplish my task. Not to mention it improves readability and makes complex PowerShell scripts manageable. This PowerShell training session introduces the concepts of a Function and a Filter. It is intended as an introduction, advance techniques will be covered in the PowerShell advanced tutorials launched early next year. With that being said, this tutorial will teach the concept of functions used for modular scripting and how to build libraries of reusable code. We will also briefly talk about filters.

Function Syntax:

Function(keyword) FunctionName (parameters) {script block}

The syntax uses the keyword Function, a FunctionName you provide, optional parameters, and the script block.

Example 1. Create a Function called “Time” which runs the Get-Date cmdlet when called.

Function Time {Get-Date}<enter>

Call the function by typing the FunctionName Time at the command prompt.

PowerShell Function

PowerShell Function

image 10.1

Simple stuff right! We defined a function using the “Function” keyword, named the function “Time,” omitted the optional parameters, and assigned the Get-Date cmdlet within the script block. Now let’s start adding some parameters.

Passing Arguments to Functions

There are multiple ways to pass arguments to a function. I’ll introduce some common methods.

Method 1: Passing Arguments in a comma separated list. Notice that the parameters enclosed in parenthesis and separated by a comma, much like an array.

Function Add ($x, $y)
{
$Ans = $x + $y
Write-Host “The Answer is $Ans”
}
PowerShell Function Add

Result

Copy the code into PowerShell and hit enter twice to get back to the command prompt. Now, at the command prompt type the function and arguments for $x and $y variables:
Add 10 2<enter>
You should see the following result – The Answer is 12

image 10.2

Method 2: Using the Param keyword. When using the Param keyword, notice that we are defining the arguments within the scriptblock {}. The Param keyword must be the first word inside the script block.

Function Add
{
param ($x, $y)

$Ans = $x + $y
Write-Host “The Answer is $Ans”
}

Type – Add 10 12<enter>

PowerShell Function Add Param

Addition

image 10.3

Method 3: If you recall from PowerShell Tutorial 7: Variables, Arrays, and Hash Tables – There is a special variable $Args which contains an array of the parameters passed to a function. Let’s use this variable to show how we can pass an argument using string expantion.

Function HAL {“What are you doing $args ?”}<enter>

Type in the FunctionName and an Argument:
HAL Dave

PowerShell Function String Extension

$Args

image 10.4

You can use multiple $Args variables to pass more than one parameter.

Example 2.

Function Add { $args[0] + $args[1] }<enter>

Type: Add 5 8

PowerShell Add Function

Multiple $Args

image 10.5

Defining Data Types

Remember the “Data Type” table that was introduced in PowerShell training session 7?

Type Description
[int] 32-bit signed integer
[long] 64-bit signed integer
[string] Fixed-length string of Unicode characters
[char] A Unicode 16-bit character
[byte] An 8-bit unsigned character
[bool] Boolean True/False value
[decimal] An 128-bit decimal value
[single] Single-precision 32-bit floating point number
[double] Double-precision 64-bit floating point number
[xml] Xml object
[array] An array of values
[hashtable] Hashtable object

PowerShell does a good job of automatically setting data types for us. Although, there may be instances were it is required to define (or hard code) the expected data type for a variable. Best practices would recommend that you always define the Data Type for a variable as it prevents parsing errors. For example, you have written a script that asks the users age. The expected result is an integer, otherwise the script would fail.

Let’s test this:

Function Age
{
Param ([int]$x)

Write-Host “You are $x years old.”
}

PowerShell Function Data Type

Data Type Error

image 10.6

Notice to two commands I ran in image 10.6.

  • Age 10 – The argument 10 is an integer and is accepted by PowerShell resulting in the output You are 10 years old.
  • Age A – The argument A is not an integer and cannot be automatically converted as we defined the variable requirement as an integer ([int]$x). Therefore PowerShell gives us the error in image 10.6.

Assigning a Default Value

We can also assign a default value should an argument not get passed to the function.

Function Add
{
Param ([int]$x = 0, [int]$y = 0)

$Ans = $x + $y
Write-Host $Ans
}

PowerShell Function Default Value

Data Type Error

image 10.7

So I created the Add function with the default variable of $x=0 and $y=0. What happens when I run the following commands:

  • Add 10 2 – Variable $x becomes 10 and $y becomes 2. The result is 12.
  • Add - Since we did not send any arguments, the default was used for $x and $y resulting in 0 + 0 = 0.
  • Add A - Again, we attempted to send an alpha character when the expected entry for the variable is an integer resulting in a conversion error. I wanted to make a point that sending the wrong data type does not result in the variable being assigned the default of 0.

Using the PowerShell Special Variable “$input”

The $input variable allows a function to access data coming from the pipeline. For example let’s say were looking for the folder “Windows” and we don’t know where it is located.

First build the function to find the Windows Folder:

Function FindFolder
{
$input | Where-Object {$_.Name -eq “Windows”}
}

Next Type the following command which pipes the cmdlet output to the function we defined:

Get-ChildItem -Path C:\ | FindFolder<enter>
PowerShell Function Find Folder

$input Results

image 10.8

So, what are we doing here? We created a function call FindFolder which uses the “Where-Object” cmdlet to filter results based on the Name property. But what results is it filtering? By itself it is just a function that does nothing until we send piped results to the $input variable.

In this example we run the Get-ChildItem cmdlet telling it to start at the root of C:\ drive. Simple enough, if we ran this command by itself we would get all the files and directories just under the root of C: drive. By piping the information to our function, the information is stored in the $input variable and only the folder named “Windows” is allow to pass.

Let me show you how we can use a function to find a needle in the haystack. In this example we will use a filter to find the Hosts file on a system.

Create the Function:

Function FindHosts
{
$input | Where-Object {$_.Name -eq “hosts”}
}

Next, since were not sure if the directory containing the hosts file is a directory under of the root of C: drive, we use the -recurse parameter to search all directories and subdirectories under the root.

Use the following command:

Get-ChildItem -Path C:\ -Recurse | FindHosts<enter>

Eventually we find the hosts file and the location Directory: Microsoft.PowerShell.Core\FileSystem::C:\Windows\system32\drivers\etc

I know that there are other ways to filter this information in PowerShell. The examples provided were to show how this task can be accomplished using functions. Next let’s talk about filters.

PowerShell Filters

A Filter and a Function are essentially the same. The difference is how they process data.

Filter Syntax:

Filter(Keyword) FilterName (parameters) {Script Block}

The syntax is the same as defining a Function except the “Filter” keyword is used, not the “Function” keyword. A function uses the $input variable to contain pipeline information whereas the filter uses a special variable $_ that contains the current pipeline object. Here is where the Function and Filter differ; The function doesn’t run until all data has been stored in the $input variable, whereas the filter has immediate access to the $_ variable and begins processing elements as they become available (streaming).

Due to the $_ variable being able to process objects immediately, filters are widely used and more efficient when large amounts of data are passed through the object pipeline.

For now, just take in the concepts. We will be working more with filters as we progress.

Real World Examples

Organizing Your Code

There are two methods I use to organize code:

  1. Modular Scripting
  2. Dot Sourcing

I find both methods useful but, most of the feedback I have gotten points to Dot Sourcing as being the most commonly used method. I’m going to show you both as I believe both methods have there own unique value.

Modular Scripting Example

I’m going to take two PowerShell script examples from the “Microsoft Script Center” and show you how to create user-defined functions and call them from a script.

Say you want to inventory your system, gathering information about the Processor(s) and Disk(s) available on the system. From the “Microsoft Scripting Center” you find these two scripts.

List Processor Information:

$strComputer = “.”

$colItems = get-wmiobject -class “Win32_Processor” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Caption: ” $objItem.Caption
write-host “CPU Status: ” $objItem.CpuStatus
write-host “Current Clock Speed: ” $objItem.CurrentClockSpeed
write-host “Device ID: ” $objItem.DeviceID
write-host “L2 Cache Size: ” $objItem.L2CacheSize
write-host “L2 Cache Speed: ” $objItem.L2CacheSpeed
write-host “Name: ” $objItem.Name
write-host
}

List Physical Disk Properties:

$strComputer = “.”

$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Description: ” $objItem.Description
write-host “Device ID: ” $objItem.DeviceID
write-host “Interface Type: ” $objItem.InterfaceType
write-host “Media Type: ” $objItem.MediaType
write-host “Model: ” $objItem.Model
write-host “Partitions: ” $objItem.Partitions
write-host “Size: ” $objItem.Size
write-host “Status: ” $objItem.Status
write-host
}

List Processor Information and List Disk Functions

In this example we are converting the ListProcessor and the ListDisk scripts, provided by Microsoft, into functions. Here are the syntaxes I’ve used: Function ListProcessor {Script Block} -and- Function ListDisk {Script Block}

Note: In PowerShell scripting, the “#” symbol is used for comments.

ListProcessor Function

# FileName: ListProcessor.txt
# =============================================================================
# FUNCTION LISTINGS
# =============================================================================
# Function: ListProcessor
# Created: [09/17/2007]
# Author: Jesse Hamrick
# Arguments:
# =============================================================================
# Purpose: Provides Processor information
#
#
# =============================================================================
function ListProcessor {
$colItems = get-wmiobject -class “Win32_Processor” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Caption: ” $objItem.Caption
write-host “CPU Status: ” $objItem.CpuStatus
write-host “Current Clock Speed: ” $objItem.CurrentClockSpeed
write-host “Device ID: ” $objItem.DeviceID
write-host “L2 Cache Size: ” $objItem.L2CacheSize
write-host “L2 Cache Speed: ” $objItem.L2CacheSpeed
write-host “Name: ” $objItem.Name
write-host
}
}

ListDisk Function

#* FileName: ListDisk.txt
#*=============================================================================
#* FUNCTION LISTINGS
#*=============================================================================
#* Function: ListDisk
#* Created: [09/15/2007]
#* Author: Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: Provides Disk Information
#*
#*
#*=============================================================================
Function ListDisk {
$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Description: ” $objItem.Description
write-host “Device ID: ” $objItem.DeviceID
write-host “Interface Type: ” $objItem.InterfaceType
write-host “Media Type: ” $objItem.MediaType
write-host “Model: ” $objItem.Model
write-host “Partitions: ” $objItem.Partitions
write-host “Size: ” $objItem.Size
write-host “Status: ” $objItem.Status
write-host
}
}

To verify functionality, you can copy and paste the functions into PowerShell and call them from the command line by name. They will enumerate the data on the local host.

Building a Modular PowerShell Script

I’m going to write a script that enumerates the processor(s) and disk(s) properties. I’ll “snap-in” my newly created functions and use the “SCRIPT BODY” section of the template to control the function calls. This may sound familiar to you if you have worked with VBScript; subroutines and functions. Couple of differences:

  1. Functions in PowerShell provide the same functionality that both subroutines and functions did in VBScript. So, subroutines do not exist in PowerShell.
  2. PowerShell parses scripts sequentially – meaning functions need to be declared before they can be called in the script. This is unlike VBScript, were the whole script is read by the scripting host before it is executed.

Here is what a modular script would look like:

#* FileName: ComputerInv.ps1
#*=============================================================================
#* Script Name: [Computer Inventory]
#* Created: [09/15/2007]
#* Author: Jesse Hamrick
#* Company: PowerShell Pro!
#* Email:
#* Web: http://www.powershellpro.com
#* Reqrmnts:
#* Keywords:
#*=============================================================================
#* Purpose: Computer Invetory of CPU and Disk Properties
#*
#*
#*=============================================================================

#*=============================================================================
#* REVISION HISTORY
#*=============================================================================
#* Date: [DATE_MDY]
#* Time: [TIME]
#* Issue:
#* Solution:
#*
#*=============================================================================

#*=============================================================================
#* FUNCTION LISTINGS
#*=============================================================================
# Function: ListProcessor
# Created: [09/17/2007]
# Author: Jesse Hamrick
# Arguments:
# =============================================================================
# Purpose: Provides Processor information
#
#
# =============================================================================
function ListProcessor {
$colItems = get-wmiobject -class “Win32_Processor” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Caption: ” $objItem.Caption
write-host “CPU Status: ” $objItem.CpuStatus
write-host “Current Clock Speed: ” $objItem.CurrentClockSpeed
write-host “Device ID: ” $objItem.DeviceID
write-host “L2 Cache Size: ” $objItem.L2CacheSize
write-host “L2 Cache Speed: ” $objItem.L2CacheSpeed
write-host “Name: ” $objItem.Name
write-host
}
}

#*=============================================================================
#* Function: ListDisk
#* Created: [09/15/2007]
#* Author: Jesse Hamrick
#* Arguments:
#*=============================================================================
#* Purpose: Provides Disk Information
#*
#*
#*=============================================================================
Function ListDisk {
$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Description: ” $objItem.Description
write-host “Device ID: ” $objItem.DeviceID
write-host “Interface Type: ” $objItem.InterfaceType
write-host “Media Type: ” $objItem.MediaType
write-host “Model: ” $objItem.Model
write-host “Partitions: ” $objItem.Partitions
write-host “Size: ” $objItem.Size
write-host “Status: ” $objItem.Status
write-host
}
}

#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
# Create a string variable using the local computer.
$strComputer = “.”

# Call the “ListProcessor” function.
ListProcessor

# Call the “ListDisk” function.
ListDisk

#*=============================================================================
#* END OF SCRIPT: [Computer Inventory]
#*=============================================================================

Now you have a visual of a modular PowerShell script. I declared the functions and used the “SCRIPT BODY” to snap the functions in place. Let’s say you want to change to order of the script, you want to list the Disk Properties before the Processor Properties. You only need to reverse the order of the function calls in the “SCRIPT BODY” to make this happen.

Modular scripting is a great way to organize your scripts. It allows you to build from a user-defined functions library, that you maintain. You can “snap-in” new functionality to existing scripts as needed and easily control the flow of your script without having to move around large chunks of code. Modular scripting may not be the best solution for the example above, the example was provided only to introduce the concept. Again, as your tasks become more complex so do your scripts. With modular scripting, complex scripts become more manageable.


Dot Source (Calling Scripts and Functions)

Essentially, “dot-source” means using dot-notation to call script blocks, functions, and/or aliases from within your script. We’re going to use the same script examples above, but instead of calling the function written within the script, we will use dot-notation to call scripts that exists outside the main script. The syntax used for dot-notation and script-blocks is:

.{}

Example 1. Calling a Script from within a Script

In the first example, I’m going to be working from the “C:\MyScripts” directory.

Step 1. Save the following code as CPU.ps1 in the MyScripts directory:

$colItems = get-wmiobject -class “Win32_Processor” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Caption: ” $objItem.Caption
write-host “CPU Status: ” $objItem.CpuStatus
write-host “Current Clock Speed: ” $objItem.CurrentClockSpeed
write-host “Device ID: ” $objItem.DeviceID
write-host “L2 Cache Size: ” $objItem.L2CacheSize
write-host “L2 Cache Speed: ” $objItem.L2CacheSpeed
write-host “Name: ” $objItem.Name
write-host
}

Step 2. Save this code as Disk.ps1:

$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2″ `
-computername $strComputer

foreach ($objItem in $colItems) {
write-host “Description: ” $objItem.Description
write-host “Device ID: ” $objItem.DeviceID
write-host “Interface Type: ” $objItem.InterfaceType
write-host “Media Type: ” $objItem.MediaType
write-host “Model: ” $objItem.Model
write-host “Partitions: ” $objItem.Partitions
write-host “Size: ” $objItem.Size
write-host “Status: ” $objItem.Status
write-host
}

Steps 1 and 2 are the same script blocks that we’ve worked with in this PowerShell Article. Now, let’s modify our main script body to use dot-notation to call these scripts.

Step3: Modify the main script as follows: Remove the function(s) code from the script and modify the script body to call both CPU.ps1 and Disk.ps1 scripts.

#* FileName: ComputerInv_v1.1.ps1
#*=============================================================================
#* Script Name: [Computer Inventory]
#* Created: [09/15/2007]
#* Author: Jesse Hamrick
#* Company: PowerShell Pro!
#* Email:
#* Web: http://www.powershellpro.com
#* Reqrmnts:
#* Keywords:
#*=============================================================================
#* Purpose: Computer Invetory of CPU and Disk Properties
#*
#*
#*=============================================================================

#*=============================================================================
#* REVISION HISTORY
#*=============================================================================
#* Date: [10/09/2007]
#* Time: [9:30 AM]
#* Issue: Dot-Source Example
#* Solution:
#*
#*=============================================================================

#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
# Create a string variable using the local computer.
$strComputer = “.”

# Use Dot-Source to Call the “ListProcessor” function.
.{.\CPU.ps1}

# Use Dot-Source to Call the “ListDisk” function.
.{.\Disk.ps1}

#*=============================================================================
#* END OF SCRIPT: [Computer Inventory]
#*=============================================================================

Since I’m working from “C:\MyScripts” directory I can use “.\CPU.ps1″ to call the scripts in my code. Run the script:

C:MyScripts\ComputerInv_v1.1.ps1<enter>

This works great if we organize all of our scripts (code library) within the “MyScripts” directory. We can use the System Path to find the .ps1 files. The $PsHome variable provides the installation path of PowerShell. By saving our scripts (code library) to this directory, we only have to call the script name without possible path issues. By default, PowerShell is installed using the following directory path: C:\WINDOWS\system32\WindowsPowerShell\v1.0

Example 2.
Step 1.
Let’s move ComputerInv_v1.1.ps1, CPU.ps1, and Disk.ps1 to the v1.0 directory. Then modify the script body as follows:

#* FileName: ComputerInv_v1.1.ps1
#*=============================================================================
#* Script Name: [Computer Inventory]
#* Created: [09/15/2007]
#* Author: Jesse Hamrick
#* Company: PowerShell Pro!
#* Email:
#* Web: http://www.powershellpro.com
#* Reqrmnts:
#* Keywords:
#*=============================================================================
#* Purpose: Computer Invetory of CPU and Disk Properties
#*
#*
#*=============================================================================

#*=============================================================================
#* REVISION HISTORY
#*=============================================================================
#* Date: [10/09/2007]
#* Time: [9:30 AM]
#* Issue: Dot-Source Example
#* Solution:
#*
#*=============================================================================

#*=============================================================================
#* SCRIPT BODY
#*=============================================================================
# Create a string variable using the local computer.
$strComputer = “.”

# Use Dot-Source to Call the “ListProcessor” function.
.{CPU.ps1}

# Use Dot-Source to Call the “ListDisk” function.
.{Disk.ps1}

#*=============================================================================#* END OF SCRIPT: [Computer Inventory]
#*=============================================================================

Here is the change in the script body – from .{.\scriptname.ps1} to .{scriptname.ps1}

Step 2. With the script files being placed in the PowerShell install directory, we only have to type in the script name in which to run. The system path will find the where the scripts are located and launch them. Let’s launch the Computer Inventory script:

C:\MyScripts\ComputerInv_v1.1.ps1<enter>

Now, CD to the root of C: drive and attempt to run the script again:

C:\ComputerInv_v1.1.ps1<enter>

Did the script work? It will use the System’s path to find the .ps1 files if necessary

Example 2. Calling a Function from another script file.

Using “dot-sourcing” you could create a library of functions. For example, let’s say you have one large file that contains over 100 separate functions you’ve created. Essentially, you maintain your function library in one or two documents. You can use dot sourcing to read the file and call one or more functions written in the file. Click here to find out how…

Which process should I use (Modular or Dot Source)?

Do I write functions within the script or do I call other scripts outside the main script? There is really no wrong or right here, it depends on the situation. For example, let’s say you are writing a script for a client that will be utilizing functions in your script library. You may wish to use the modular approach so that you can present your client will a single script that is well document. If your writing scripts for you own environment, chances are your using dot sourcing to call other functions as it takes less time and less code to accomplish the task…

Conclusion

In this PowerShell Training session we have:

  • Defined Functions and Filters.
  • Learned how to create and call Functions.
  • Passed arguments to Functions.
  • Introduced the param keyword and the $args variable.
  • Talked about how to define data types.
  • Explained how to set default parameters.
  • Touched on the differences between Functions and Filters.
  • The $input variable and the $_ variable.
  • Examples of using Functions to organize scripts and control script execution.

Again, the purpose of this tutorial was the introduction of Functions and Filters and how they can assists with script organization. Functions and Filters provide many uses as we will discover as we progress. These uses will become known throughout the rest of the tutorials.

Until next time, happy scripting…

Email This Page To A Friend Email This Page To A Friend

Comments

Is there a way to call a function that resides in a separate script, along the lines of Perl’s use statement? I don’t want to have to load each function script manually into the current PS session.

By Tom Moreau on January 7th, 2008 at 1:59 pm

That’s close to what I’d like. Ideally, I’d like to refer to the script name just once and then refer to the functions therein as required. I’m wondering what to do if I have a .PS1 script that contains multiple functions and I want to execute a specific function within that PS1 script.

By Robert Brault on March 25th, 2008 at 7:48 pm

Jesse, I am looking for a way to build a dynamic array list that a user can select an item from that list and then the script would perform a function against that selected item. The data I am recieving resembles the data in Image 10.8, where the data has table header information. I cannot seem to figure out how to build the array from the dynamic data. Maybe it makes sense to let you know I am trying to get a list of the latest recovery points in DPM so that I can remove a selected recovery point.

I feel so accomplished. I re-wrote the FindFile function just to see how much I’ve retained so far and this is what I came up with:

Function FindFile
{
param($fileToFind)
$input = get-childitem -path c:\ -recurse
$input | where-object{$_.Name -eq “$fileToFind”}
}

The first time I used the function it took a little while before $input got loaded, but the second time around, it was much quicker.

I love this stuff. Can’t wait to get into the WMI scripting.

By sanjeev on May 8th, 2008 at 6:11 am

could you please explain something about functions at runtime level. because i tried overloading the function but it is only taking the latest version and not overloading at all.
i loved the tutorial.the method and the content is just perfect

Is “snap-in” just copying one text file into another one? (Coming from a Unix environment, some of the Windows jargon is unfamiliar.)

@hrp2171 – If I understand PS correctly, the line “$input = get-childitem -path c:\ -recurse” assigns a huge but temporary amount of data to $input, which has to be completed before you go on to the filtering. It would be much faster to do without the $input variable and just pipe the get-childitem directly into the where-object.

One question which I have answered by experiment is to do with the scope of variables when functions are called. For example, what output would you expect from this:
$x = 3
function aaa {
$x += 1
bbb
write-host “aaa: $x”
}
function bbb {
$x += 1
write-host “bbb: $x”
}
$x
aaa
$x

The answer is that when you refer to a variable in a function, you get the variable that was in scope at the function call (ie dynamic, not lexical scoping) and if you declare a variable in a function it has local scope and masks any same-name global variable. This may seem rather arcane but you need to know these things to tell what will happen!

I wrote test.ps1 which contains following line -
function Hello-World { Write-Host “Hello World” }

Then, I wrote caller.ps1 which contains following line -
.{‘.\test.ps1′}

Both the scripts are in the same folder. When I run caller.ps1 file, it does not show “Hello World” on screen.

Please tell me where I was wrong.

The issue you are running into is a syntax problem. You are attempting to call a FUNCTION using the syntax for calling another powershell script. Let me give you examples of how to do both:
Calling a script that lives outside the main script.
Test.ps1 code -
Write-Host “Hello World”

Caller.ps1 code -
.{.\test.ps1}

Calling a Function that lives outside the main script.
Test.ps1 code -
function Hello {Write-Host “Hello Nitin”}

Caller.ps1 code -
. .\test.ps1
Hello

Make sure you understand when you are attempting to call a script or a function within a script that live outside the main script. This tutorial talks about calling functions but you have to follow “click here to find out how” link under Example 2. Calling a Function from another script file.

Jesse, thanks for this excellent tutorial. This is at the perfect level for me, just beginning. Very easy read to get started. …on to the next one WMI!!

Can someone explain this odd behavior to me?

#—
function Test($a, $b) {Write-Host “a=$a, b=$b”}
Test(“one”, “two”)
#—

I would have expected the above to display “a=one, b=two”, but instead it displays “a=one two, b=”. Why is this? How would I pass two strings to a function and get the results I expected?

I’m playing with the following script to find files older than 1 day.

$DateToCompare = (get-date).AddDays(-1)
$logFiles = get-childitem C:\logs -recurse | ? {$_.LastWriteTime -lt $DateToCompare}
foreach ($f in $logfiles){
Write-Host $f.name
}

I can send the results via email, however, I need the name and lastwritetime of each file on a new line in the variable sent to the email function. In vb it would be something like:

foreach f in fld.files
fileformat = fileformat & vbcrlf
next
SendMail fileformat

Anyone know how this is done in PS? Cheers J

This seems to work:
function Test($a, $b) {Write-Host “a=$a, b=$b”}
Test ”one” “two”

I don’t know why, but powershell seems to dislike the () and , seperators.

Seems like there is a short-hand way of creating arrays:

PS > $a=”one”, “two”
PS > $a
one
two
PS > $a[0]
one
PS > $a[1]
two

Gary:

You probably already figured this out, but the reason why your test script gives seemingly unusual results is that your input is an array. I.e., the comma is actually an operator which means “this is an array” so when you provide input as “one”, “two” you provided an array. Since PS is dynamically typed, your first parameter $a got an array, not a scalar.

Similarly, this explains why Jeff’s script worked – he provided 2 scalars since there was no comma separating the input.

The lesson? Don’t use commas on the command line unless you mean to provide an array.

By Juan Trujillo on October 9th, 2009 at 9:52 am

Hi,
Is it possible to get a list of the Custom functions/objects/variables that I have created on my current session ?

Thanks in advance.

Hi,

I get errors running the powershell unless I use a ./ Why???

Thanks.

Hi,

The site is really interesting..

Can anyone tell me how to compare two local folders, the one similar to Get-Diff?

Thanks.

I’m reading this site with Safari on a Mac.

When I tried to copy and paste the ListDisk function or the ListProcessor function into a Powershell window on Parallels running Windows 7, I kept getting a >> continuation prompt that wouldn’t go away until I pressed Control C and the functions wouldn’t be created.
I tracked it down to a double quote character following the quoted:“root\CIMV2? <—<> prompt.
I post this, in case anyone else runs into similar problem and something like this may be the answer.

I’m reading this site with Safari on a Mac.

When I tried to copy and paste the ListDisk function or the ListProcessor function into a Powershell window on Parallels running Windows 7, I kept getting a >> continuation prompt that wouldn’t go away until I pressed Control C and the functions wouldn’t be created.
I tracked it down to a double quote character following the quoted:“root\CIMV2?
That double quote character, which was different from the others, would not paste into the Powershell window and Powershell would keep expecting a ending-double-quote, and therefore repeatedly gave the >> prompt.
I post this, in case anyone else runs into similar problem and something like this may be the answer.

I’m reading this site with Safari on a Mac.

When I tried to copy and paste the ListDisk function or the ListProcessor function into a Powershell window on Parallels running Windows 7, I kept getting a >> continuation prompt that wouldn’t go away until I pressed Control C and the functions wouldn’t be created.
I tracked it down to a double quote character following the quoted:”root\CIMV2″ <–<> prompt.
I post this, in case anyone else runs into similar problem and something like this may be the answer.

I’m reading this site with Safari on a Mac.

When I tried to copy and paste the ListDisk function or the ListProcessor function into a Powershell window on Parallels running Windows 7, I kept getting a double right angle bracket continuation prompt that wouldn’t go away until I pressed Control C and the functions wouldn’t be created.
I tracked it down to a double quote character following the quoted:”root\CIMV2″ <–<<
That double quote character, which was different from the others, would not paste into the Powershell window and Powershell would keep expecting a ending-double-quote, and therefore repeatedly gave the double right angle bracket continuation prompt.
I post this, in case anyone else runs into similar problem and something like this may be the answer.

I’m reading this site with Safari on a Mac.

When I tried to copy and paste the ListDisk function or the ListProcessor function into a Powershell window on Parallels running Windows 7, I kept getting a double right angle bracket continuation prompt that wouldn’t go away until I pressed Control C and the functions wouldn’t be created.
I tracked it down to a double quote character following the quoted:”root\CIMV2″
That double quote character, which was different from the others, would not paste into the Powershell window and Powershell would keep expecting a ending-double-quote, and therefore repeatedly gave the double right angle bracket continuation prompt.
I post this, in case anyone else runs into similar problem and something like this may be the answer.

Whoops!
I kept getting a error page when I tried to submit a comment. I didn’t realize the comments where actually accepted despite the errors mentioned on the error page I was seeing, so I kept submitting variations of my comment.

Can’t I put functions at the end of the file instead of stating it up front?

@chris: Yep. At least, I was able to using the Powershell ISE.

@Russ – Thanks, your solution did help me as I was having the same issue.

I am working with the ListCPU script, and when I run it I get

The string starting:
At C:\mytest\CPU.ps1:13 char:19
+ write-host “Name: <<<< " $objItem.Name
is missing the terminator: ".
At C:\mytest\CPU.ps1:19 char:1
+ <<<<
+ CategoryInfo : ParserError: ( $objItem.Name
write-host
}

:String) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString

as you can see It is coming back with what to me seems an ambiguous error. If somebody got the script working, please help me.

Thanks in advance.

I am getting the same error as Wally S., however, I am running listDisk and listProcessor when the error appears. I tried moving
$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2? `
-computername $strComputer
into a single line and removing the ” ‘ “, cause that seemed to be causing a prob…but still, no dice….

Notice how, when I copy and paste the $colItems line, that a (?) is shown instead of the closing (“). Odd.

OK..For anybody else having the prob that I was having…I was initially using a single quote(‘) instead of a tick(`). But, after inserting the correct tick, I had to erase and re-insert the previous double-quotes(“) before it would work..Now….moving on!!!

thanks for this great article was a eye opner for me many thanks and keep up the good work :-)

By NomadAbhi on April 4th, 2012 at 10:08 pm

Running in ISE:
C:\Documents and Settings\XXXXXXXX> $strComputer = “.”

$colItems = get-wmiobject -class “Win32_DiskDrive” -namespace “root\CIMV2? -computername $strComputer

foreach ($objItem in $colItems) {
write-host “Description: ” $objItem.Description
write-host “Device ID: ” $objItem.DeviceID
write-host “Interface Type: ” $objItem.InterfaceType
write-host “Media Type: ” $objItem.MediaType
write-host “Model: ” $objItem.Model
write-host “Partitions: ” $objItem.Partitions
write-host “Size: ” $objItem.Size
write-host “Status: ” $objItem.Status
write-host
}

# Getting following output. Any idea?

The string starting:
At line:13 char:21
+ write-host “Status: <<<< ” $objItem.Status
is missing the terminator: ".
At line:17 char:1

By NomadAbhi on April 4th, 2012 at 10:46 pm

Nevermind, I see the culprit “root\CIMV2? instead of “root\CIMV2″
Funny that in ISE it was still showing ” instead of ? unless I copied the code to Notepad to observe.

Proceeding ahead…

By elmo533940 on May 19th, 2012 at 9:17 am

The examples in this lesson were copied & pasted into both the PS ISE and the regular PS. Nowhere did they work.

The problem is the code on this page is in Unicode. You copy and paste it into notepad or whatever and it does some very strange things when turning into ASCII

I appreciate the time you took to write this helpful and detailed article back in 2008. I am sure it will be helping people for many years to come!

By Adil Hindistan on October 29th, 2012 at 10:10 am

@Chris – If you are using PowerShell ISE, it does not matter where the functions are located. This is not the case if you are trying to run PS scripts from command line and you will get an error telling you that the function you are calling is undefined.

This article is still helping me get started using PowerShell and it’s 5 years old… Great work! The terminator (“.) issue is solved when you swap our the double quotes for singe quotes… Keep enjoying the ride everyone!

You wrote:

I find both methods useful but, most of the feedback I have gotten points to Dot Sourcing as being the most commonly used method. I’m going to show you both as I believe both methods have there own unique value

Should be “their own value”. Thanks.

Great tutorials despite some spelling errors. I have come across a few but did not think of sending them to you. Will do so if I find any others.

By atesgautama on March 11th, 2014 at 6:25 am

In the examples above, retype the double quotes in notepad rather than Word (which does automatic changes).

As you might expect a variable defined in one function will not be availible in another. What you might need is the returning value of a function into another function. In Powershell you do not need to call the first function. Just call the seconde one with the first as input value.
————————————-
Function StartingMain {
$a = 1 + 3
Return $a
}
Function StartingEnd($b) {
Write-Host $b
}
StartingEnd(StartingMain)
————————————-

Note that you have to call the funtion below the function itself otherwise the first time it runs it will return an error that it doesn’t know what “StartingMain” is.

I just don’t understand what the $args variable does. I’m not a scripter, and I can’t google an explanation that makes any sense. Would someone be willing to break it down for me?

In C/C++ I can do something like this:
//Function declarations
int func();

int main()
{
int x = func();
}

//Function definitions
int func()
{
return 2;
}

Is there a way to do something similar in powershell?

Code below does not work.

#Function declarations
Function Add ($a, $b);

Write-Host Add 3 6

#Function definitions
Function Add ($a, $b)
{
return $a+$b;
}

Somehow powershell wants all function definitions before function calls. Can is there a way to work around this?

#Function definitions
Function Add ($a, $b)
{
return $a+$b;
}

Write-Host Add 3 6

I’m a PS noob so go easy on me :)

 

Leave a Comment