Calling a function within module / Powershell - powershell

I must be missing something basic here, but i'm new to powershell...
I wrote a function and saved it in a file called "UserSelectionList.psm1", the function is stubbed out like this:
function Global:UserSelectionList([String[]] $UserOptions)
{
...
}
i then try to call it with this script:
Import-module "l:\support downstream\solarc\cngl\powershell scripts\userselectionlist.psm1"
$Options = "a","b","c"
cls
$result = UserSelectionList $Options
echo $result
The resulting error is:
The term 'UserSelectionList' 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:5 char:28
+ $result = UserSelectionList <<<< $Options
+ CategoryInfo : ObjectNotFound: (UserSelectionList:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I'm planning to have more than one function in a module, but this is where I'm at.
thanks in advance

I've encountered the same problem. Steps to reproduce:
Write a PowerShell script with an Import-Module statement
Execute the script at a PowerShell prompt
Add a function to the imported module
Modify the script to call the newly-added function
Execute the script again, in the same PowerShell session
The error went away after I added the -Force argument to Import-Module. The -Force argument can be removed once the function in the imported module is able to be called.
Note that latkin has alluded to this solution in his comment to the question. I'm hoping this will make it more visible.

You only need to Get-Command if you didn't export the method properly from the module.
At the end of your module put this:
Export-ModuleMember -Function UserSelectionList
Note that it also accepts wild cards, so for example if you have 5 different Get-Some-Value functions that follow a naming convention you could just do
Export-ModuleMember -Function Get-*
Side note on -Force: all that does is check if a module is already loaded and, if it is, removes it before continuing with the import. It's the same as saying:
Remove-Module userselectionlist.psm1
Import-Module userselectionlist.psm1

[Edit] I was not doing a Import Module with a -Force option. The answer below is incorrect, but perhaps the Get-Command forced a refresh? Either way, I'm leaving it for completeness of the experience!
Thanks to latkin for pushing me to another path where i found this:
How do I retrieve command from a module
Not only do you have to import a module, you then have to "get" it as well (?)
Import-Module -Name <ModuleName>
Get-Command -Module <ModuleName>
After I issued the Get-Command, everything started to work!
Thanks latkin for quick response!

Related

Can't declare methods in a script being run in PowerShell

I'm invoking a script as described here and exemplified here. It's running and I can see the outputs to the console (both hazaa and shazoo). However, the method declared in it does not appear to be there, despite testing both versions below.
Invoke-Expression -Command $target
$target | Invoke-Expression
The contents of the file are like this.
Write-Host "Hazaa"
function TestPower { Write-Host "I got the power..." }
Write-Host "Shazoo"
When I execute the same function definition in the console, it's there, working fine. I'm not getting any errors on the manual execution nor the invokation from the other file. No warnings neither.
The weirdest part is that the function definitions done in the invoking script (i.d. the one that the invokes the execution not the one that in being targeted for the invokation).
To rephrase your issue - a function defined in a script invoked with Invoke-Expression isn't available after the expression has executed, i.e.:
test.ps1
Write-Host "Hazaa"
function TestPower { Write-Host "I got the power..." }
Write-Host "Shazoo"
Interactive
PS> Invoke-Expression -Command "C:\src\so\test.ps1"
PS> TestPower
TestPower : The term 'TestPower' 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:1
+ testpower
+ ~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (testpower:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
You can fix this by dot-sourcing the script in the expression instead:
PS> Invoke-Expression -Command ". 'C:\src\so\test.ps1'"
Hazaa
Shazoo
PS> TestPower
I got the power...
Note that the documentation for script scope and dot sourcing says:
Each script runs in its own scope. The functions, variables, aliases, and drives that are created in the script exist only in the script scope. You cannot access these items or their values in the scope in which the script runs.
Which explains why TestPower isn't available outside of your Invoke-Expression because the script is running in its own scope.
By contrast, using dot sourcing:
The dot sourcing feature lets you run a script in the current scope instead of in the script scope. ... After the script runs, you can use the created items and access their values in your session.

How to expose sub module function, from a module

How do you expose a function and an alias from a module, from a sub module
profile.ps1:
Import-Module module_one.psm1
module_one.psm1:
Import-Module module_two.psm1:
module_two.psm1:
Set-Alias readme -Value "Read-Me"
function Read-Me() {
Write-Host "Hello..."
}
Export-ModuleMember -Function Read-Me, readme
I want to be able to call this function from the pwsh terminal, function name or alias?
I get (readme or Read-Me):
Read-Me : The term 'Read-Me' 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:1
+ co
+ ~~
+ CategoryInfo : ObjectNotFound: (co:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Note: it works for functions in module_one.psm1.
Assuming that you call Import-Module module_one.psm1 and later readme / Read-Me in the same scope domain (either outside a module or from the same (other) module), your code works in principle:
The exported elements of the indirectly imported module are (also) imported into the top scope of the caller's scope domain.
Your problem is that you're trying to export alias readme as a function; you need to use the separate -Alias parameter instead:
Export-ModuleMember -Function Read-Me -Alias readme # Note the -Alias parameter
Also note that in the absence of an Export-ModuleMember call it is all (top-level) functions and aliases (but not variables) that are exported by default, so in your particular case simply omitting the call would have fixed the problem too.
In general, though, it is better to be explicit about what elements are exported, and that is best done via a full-fledged module that is not just a single *.psm1 file, but an entire directory named for the module, housing the *.psm1 file with an associated module manifest, which is a *.psd1 file that describes the module, notably also in terms of its exports.
If such a module is placed in one of the directories listed in $env:PSModulePath, its exported commands can be discovered (e.g., with Get-Command or tab-completion) even before the module is imported, via a feature called auto-loading - see about_Modules.
Note that Import-Module -Global is rarely needed and should generally be avoided, because it makes a module's exported elements available to all loaded modules too, which can have unintended side effects (modules should declare their dependencies explicitly).

Powershell spaces in script path

I am trying to streamline how I execute some scripts I wrote by setting up a function and alias to run them. I currently have functions to change my directory to where the scripts need to be run, but when I try to run the script itself I get the following error:
C:\Users\me\Desktop\BoB : The term 'C:\Users\me\Desktop\BoB' 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:1
+ C:\Users\me\Desktop\BoB Tools\folderScannerV0.4.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\jteit\Desktop\BoB:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
The function is:
function run-scanner { "& 'C:\Users\me\Desktop\BoB Tools\folderScannerV0.4.ps1'" | Invoke-Expression }
I've tried a few variations based on other answers I've found, but I keep getting the same error. I would prefer to not remove the space on the path because other scripts use it successfully. Running the script from the ISE gives me no problems.
Ideally I would like the function to also allow me to have the script run on the folders I would like without changing the working directory (each script works on a particular set of files that are in a static location but some of them use $PWD to get the folders in the location).
For example in my $profile file I have this function: function go-to-temp {cd "C:\Users\me\Desktop\Bob Tools\To be Formatted\Temp"} which I run before I execute the above script. I would like them rolled into a single command without my working directory changing (which would render the go-to-temp function redundant.
What am I doing wrong?
There is no reason to run your script through Invoke-Expression.
Unless your script relies on $PWD, then you should be able to execute it with the call operator: &. As the other poster mentioned, you can use dot-sourcing (.) if you need the variables the script generates, but this will import all global objects (aliases, variables, functions) to your current scope. If it does rely on $PWD, you can utilize Start-Process with -WorkingDirectory to avoid changing where you're at.
function Start-Scanner {
& "$HOME\Desktop\BoB Tools\folderScannerV0.4.ps1"
}
or
function Start-Scanner {
$startArgs = #{
FilePath = "$PSHOME\powershell.exe"
ArgumentList = '-File', "`"$HOME\Desktop\BoB Tools\folderScannerV0.4.ps1`""
WorkingDirectory = "$HOME\Desktop\BoB Tools"
NoNewWindow = $true
Wait = $true
}
Start-Process #startArgs
}
You can just use dot-sourcing for this:
function run-scanner { . 'C:\Users\me\Desktop\BoB Tools\folderScannerV0.4.ps1' }

Comment Based Help not displayed in Powershell

I am trying to implement Comment Based Help as described here with Powershell 5.1 on Windows Server 2016.
The script I am using is called blah.ps1:
<#
.DESCRIPTION
blah blah
#>
function Blahblah
{
}
In Powershell, I can load the script:
.\blah.ps1
But when I ask for help on this function using:
Get-Help Blahblah
Powershell only reports an error:
Get-Help : Get-Help could not find Blahblah in a help file in this session. To download updated help topics type: "Update-Help". To get help online, search for the help topic in the TechNet library at http://go.microsoft.com/fwlink/?LinkID=107116.
At line:1 char:1
+ Get-Help Blahblah
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (:) [Get-Help], HelpNotFoundException
+ FullyQualifiedErrorId : HelpNotFound,Microsoft.PowerShell.Commands.GetHelpCommand
I am assuming that Get-Help can be used with the custom help comments. What am I missing?
Import the script as a module instead of running it.
Import-Module .\blah.ps1
While declaring the function, including the help immediately after the function header, saving it in a module, and importing the module is probably the best way to handle it, you can make your script's help available to Get-Help by ensuring that...
the function name and the file name match (which means one function per file), and...
ensuring that the script file is in a directory that is in $env:PATH.
The help for the function can be declared before the function itself is, provided that there is no more than one blank line between the help's closing #> and the beginning of the function declaration.
See Get-Help about_Comment_Based_Help in either the PowerShell help or the linked Microsoft documentation.

Powershell module not loading

I am trying to load a PowerShell module that executes a custom cmdlet function but I can't get it to load... I've applied the solutions of several previous questions, but now I'm just going in circles. Here are the specs of what I have done so far and the specific error that returns. Note that as I am new to PowerShell and programming in general, it wouldn't surprise me that my problem isn't a file path issue but a logic error in my actual function:
I created a profile function for a custom cmdlet that allows me to
open project files from two different paths:
function push-project( $project )
{
pushd ~/Documents/Visual Studio 2015/Projects/$project;
pushd ~/Documents/GitHub/$project;
}
New-Alias -Name pp -Value push-project;
I created a module by saving the function as ProfileFunctions.psm1
in the following directory:
~\Documents\WindowsPowerShell\Modules\ProfileFunctions\ProfileFunctions.psm1
To invoke the function, per its syntax, I type in pp $projectName into the PS console window, but the error that returns is standard not recognized:
pp : The term 'pp' 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:1
pp MyFirstApp
~~
CategoryInfo : ObjectNotFound: (pp:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
I copied and pasted your code into a Windows 8.1 machine I have here. It worked great (apart from the folder names not existing, since I don't have Visual Studio).
Things off the top of my head that might stop your module from working:
The file is actually called ProfileFunctions.psm1.ps1 or ProfileFunctions.psm1.txt or something.
The Modules folder is saved in someone else's documents folder, or the Public documents folder.
You've accidentally put a space in the folder name (it must be WindowsPowerShell, not Windows PowerShell).
I Think your problem is that the Alias pp is not exported from the module.
You either define the alias outside the module, as supposed to or explicitly export it from the module using.
Export-ModuleMember -Function pushproject -Alias pp
Find more details in this article Unable to Create a Powershell Alias in a script Module