willem
willem

Reputation: 27027

In PowerShell, how do I define a function in a file and call it from the PowerShell commandline?

I have a .ps1 file in which I want to define custom functions.

Imagine the file is called MyFunctions.ps1, and the content is as follows:

Write-Host "Installing functions"
function A1
{
    Write-Host "A1 is running!"
}
Write-Host "Done"

To run this script and theoretically register the A1 function, I navigate to the folder in which the .ps1 file resides and run the file:

.\MyFunctions.ps1

This outputs:

Installing functions
Done

Yet, when I try to call A1, I simply get the error stating that there is no command/function by that name:

The term 'A1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling
 of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:3
+ A1 <<<<
    + CategoryInfo          : ObjectNotFound: (A1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

I must misunderstand some PowerShell concepts. Can I not define functions in script files?

Note that I have already set my execution policy to 'RemoteSigned'. And I know to run .ps1 files using a dot in front of the file name: .\myFile.ps1

Upvotes: 337

Views: 363547

Answers (9)

Carl Walsh
Carl Walsh

Reputation: 6999

The simplest change is to set the A1 function in the global scope

MyFunctions.ps1

function global:A1
{
    Write-Host "A1 is running!"
}

Then it works:

$ ./MyFunctions.ps1
$ A1
A1 is running!

Upvotes: 1

David Morrow
David Morrow

Reputation: 294

You can add function to:

c:\Users\David\Documents\WindowsPowerShell\profile.ps1

An the function will be available.

The actual location of the profile file is determined by the $PROFILE variable and typing just that into a powershell terminal will tell you the file to edit.

Upvotes: 9

Hemendr
Hemendr

Reputation: 1060

Let's say I created a script at path C:\Temp\TestScript.sp1

enter image description here

Scripts and functions within those scripts follow the rules of scope.

enter image description here

Also When you use the call operator (&) to execute a script file in powershell editor, it is also not added to the current scope of powershell editor as shown below enter image description here

Upvotes: 7

rsc
rsc

Reputation: 4444

Try this on the PowerShell command line:

. .\MyFunctions.ps1
A1

The dot operator is used for script include, aka "dot-sourcing" (or "dot source notation")

Upvotes: 342

JoeG
JoeG

Reputation: 4247

What you are talking about is called dot sourcing. And it's evil. But no worries, there is a better and easier way to do what you are wanting with modules (it sounds way scarier than it is). The major benefit of using modules is that you can unload them from the shell if you need to, and it keeps the variables in the functions from creeping into the shell (once you dot source a function file, try calling one of the variables from a function in the shell, and you'll see what I mean).

So first, rename the .ps1 file that has all your functions in it to MyFunctions.psm1 (you've just created a module!). Now for a module to load properly, you have to do some specific things with the file. First for Import-Module to see the module (you use this cmdlet to load the module into the shell), it has to be in a specific location. The default path to the modules folder is $home\Documents\WindowsPowerShell\Modules.

In that folder, create a folder named MyFunctions, and place the MyFunctions.psm1 file into it (the module file must reside in a folder with exactly the same name as the PSM1 file).

Once that is done, open PowerShell, and run this command:

Get-Module -listavailable

If you see one called MyFunctions, you did it right, and your module is ready to be loaded (this is just to ensure that this is set up right, you only have to do this once).

To use the module, type the following in the shell (or put this line in your $profile, or put this as the first line in a script):

Import-Module MyFunctions

You can now run your functions. The cool thing about this is that once you have 10-15 functions in there, you're going to forget the name of a couple. If you have them in a module, you can run the following command to get a list of all the functions in your module:

Get-Command -module MyFunctions

It's pretty sweet, and the tiny bit of effort that it takes to set up on the front side is WAY worth it.

Upvotes: 305

Bytekoder
Bytekoder

Reputation: 302

Assuming you have a module file called Dummy-Name.psm1 which has a method called Function-Dumb()

Import-Module "Dummy-Name.psm1";
Get-Command -Module "Function-Dumb";
#
#
Function-Dumb;

Upvotes: 2

bergmeister
bergmeister

Reputation: 969

If your file has only one main function that you want to call/expose, then you can also just start the file with:

Param($Param1)

You can then call it e.g. as follows:

.\MyFunctions.ps1 -Param1 'value1'

This makes it much more convenient if you want to easily call just that function without having to import the function.

Upvotes: 8

yzorg
yzorg

Reputation: 4480

. "$PSScriptRoot\MyFunctions.ps1" MyA1Func

Availalbe starting in v3, before that see How can I get the file system location of a PowerShell script?. It is VERY common.

P.S. I don't subscribe to the 'everything is a module' rule. My scripts are used by other developers out of GIT, so I don't like to put stuff in specific a place or modify system environment variables before my script will run. It's just a script (or two, or three).

Upvotes: 22

Jonny
Jonny

Reputation: 2683

You certainly can define functions in script files (I then tend to load them through my Powershell profile on load).

First you need to check to make sure the function is loaded by running:

ls function:\ | where { $_.Name -eq "A1"  }

And check that it appears in the list (should be a list of 1!), then let us know what output you get!

Upvotes: 7

Related Questions