Reputation: 33
It's already a while since I just started to get along with PowerShell and by today I can say I get many things for my daily tasks done. Everyday is a new day at school though and now, while I was starting to work with functions, I stumble again... Here is my problem:
Even with the simplest function taken from many beginner's tutorials it is not working on my work PC nor on my home PC nor on my work's admin servers.
For instance:
function MyDate {
Get-Date
}
Saved the PS1 file with the name MyDate.ps1 to a local folder and just run this tiny little thing from the PowerShell prompt like that
PS C:\PS> C:\PS\MyDate.ps1
That's how it was exactly explained in a PS-Functions-for-Dummies tutorial. Just in the tutorial the output should show the date. In my siutation there is just nothing. No error, no red-PS-specific information, nothing! If I use the function within my full script like that:
function MyDate {
Get-Date
}
MyDate
PS C:\PS> C:\PS\MyDate.ps1
Montag, 1. Oktober 2018 19:28:32
Well, then I get the date output. Wondering why not directly from the prompt without calling the function within the script, as functions are supposed to be used? And again, in many of these pretty simple examples it is supposed to work.. Where am I wrong then?
Edit: Not sure if this is now the right policy to add more updates to a question?
I will probably share a more specific example of my scripts and how I want to "transform" it into a function. Short example would be:
$myServers = Get-Content D:\Servers\Windows2016.txt
foreach ($myServer in $myServers) {
Get-Printer -ComputerName $myServer |
Where-Object {$_.DeviceType -eq "Printer"} |
Select Name, PortName
}
For other support teams it has to be as simple as possible. So my idea was to "wrap" a function around these few lines, so that another person would execute it like this
C:\Scripts\Our-Printers -File D:\Servers\Windows2016.txt
Our-Printers
would then be my function name of course.
I tried that already but discarded the stuff though as it did not work in first try, that's why I wanted to start with pretty basic stuff and at the moment it seems that I don't get it. But by the way, within a script my functions worked already, just they are not made to require input in that way of the example. Which means I defined functions in a large script and with that I am able to change orders or even skip certain functions based on user input...
Upvotes: 2
Views: 2548
Reputation: 200523
Defining a function in a script means you write a block of code that can be invoked via a symbolic name. The function is not automatically invoked by running the script, though. You still need to actually do that.
PS C:\> Get-Content .\a.ps1 function GetDate { Get-Date } PS C:\> .\a.ps1 PS C:\> Get-Content .\b.ps1 function GetDate { Get-Date } GetDate # ← actually invoke the function PS C:\> .\b.ps1 Montag, 1. Oktober 2018 19:50:32
Only (script-)global scope code in a script is executed automatically when the script is invoked.
Depending on the purpose of your script this may or may not be what you want. For instance, if you intend to just run the script from some external script or command to have it do something it makes sense to automatically invoke the function when the script is executed. If on the other hand you intend to use the script as a kind of library, storing different functions that you want to be able to invoke separately, you'd omit function invocations from the script. Then you can dot-source the script as Mathias described and invoke the function(s) you need.
PS C:\> Get-Content .\a.ps1 function GetDate { Get-Date } PS C:\> . .\a.ps1 # ← load the function definition into the current scope PS C:\> GetDate # ← actually invoke the function Montag, 1. Oktober 2018 19:50:32
Edit: Your ultimate goal could be achieved either way. There are files (called profiles) that PowerShell sources automatically when launched. You could define a function and put that in the appropriate profile (per user or system-wide). After restarting PowerShell people will then be able to invoke the function.
Alternatively you could put the code in a script (without wrapping it in a function), and put that script someplace where the intended users can execute it.
Which of these two approaches is preferable for your situation depends on a number of factors. Do you have an domain or a workgroup? In case of a domain, can you make changes to the AD? Do you have a shared location where you could put files? Do you have a software deployment system? And so on.
Another important question is: will the users invoke the code just from PowerShell or also from elsewhere (e.g. CMD). In case of the latter one would normally prefer a script over a function in a profile.
Either way you'd parametrize your script or function like this:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$File
)
$myServers = Get-Content $File
foreach ($myServer in $myServers) {
Get-Printer -ComputerName $myServer |
Where-Object {$_.DeviceType -eq "Printer"} |
Select-Object Name, PortName
}
The parameter definition belongs at the beginning of the script or function respectively.
Add comment-based help, so that people can run Get-Help Our-Printers
or Our-Printers -?
to see how they're supposed to use it, e.g.
<#
.SYNOPSIS
List printers.
.DESCRIPTION
List printers. The computers to check are read from a file.
.PARAMETER File
A file with a list of computer names (one per line).
#>
Use parameter validation for enforcing valid input, e.g.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[ValidateScript({Test-Path -LiteralPath $_})]
[string]$File
)
Upvotes: 5
Reputation: 175085
When you have a function definition in a script file, and you want to "import" that function, you need to use the dot source (.
) operator:
PS C:\PS> . C:\PS\MyDate.ps1
PS C:\PS> MyDate
Montag, 1. Oktober 2018 19:39:18
The difference between using the .
operator and just calling the script by name, is that the .
operator runs the script in the calling scope - meaning that the function definition persists in your powershell session after the script stops executing.
You can read more about scopes in the about_Scopes
help file
Upvotes: 3