This is for readability and safety (clobbering duplication functions) and finding a workaround for no namespace support with PowerShell modules.
I want to be able to do something like this:
Import-Module MyHelpers.psm1 -Functions "FuncOne" -as MyHelpers.Func-One
MyHelpers.Func-One -blah sfsdfsdf
This make it obvious where FuncOne lives. for larger scripts I consider this pretty serious requirement.
It would probably be good enough if I could at least explicitly define which functions I'm importing (without being able to rename them). At least I would see where they are coming from. Is there any support for this? If not then I'll just have to name all functions inside of MyHelpers like MyHelpers.Func-One but then PowerShell will complain the verb is wrong; would that also break other things too?
Here's my comment as answer:
You can use the Function parameter to restrict the function (s) you want to import.
Next, with the Prefix parameter you can add a prefix string to these imported functions.
See Import- module examples 5 and 6
Theo's answer is correct, just want to point out also that you can fully qualify commands that you call by prefixing them with the module name already, for example:
Microsoft.PowerShell.Core\Import-Module -Name ActiveDirectory
ActiveDirectory\Get-ADComputer $env:COMPUTERNAME
or to your example:
Import-Module MyHelpers.psm1 -Functions "FuncOne" -as MyHelpers.Func-One
MyHelpers\Func-One -blah sfsdfsdf
Related
When I write comment based help in a PowerShell CmdLet ps1 script,
that is contained in a module, is it possible to refer to other
CmdLets in the same module so that the resulting output to the user
will print the name of the referred CmdLet when imported with a
prefix?
So for example, if I write my comment based help like this:
function Get-Thing {
<#
.SYNOPSIS
Get the thing.
.DESCRIPTION
The Get-Thing CmdLet will get a thing.
#>
[CmdletBinding()]
Param(...)
and the user imports the module using a prefix:
Import-Module -Prefix My
Then I want the help to print the CmdLet name Get-Thing in the description field as Get-MyThing, honoring the module prefix value that the user provided:
> help Get-MyThing
NAME
...
SYNOPSIS
...
SYNTAX
...
DESCRIPTION
This Get-MyThing CmdLet will get a thing.
Is this possible?
The NAME and SYNTAX sections will be automatically updated to include the prefix when you use one. It's not possible to change it if you use it elsewhere in the help text because you cannot embed variables in a comment.
I would just suggest avoiding using the cmdlet name elsewhere where you can, although you'll generally want to in Examples and that will then be unchanged. However at least with the NAME and SYNTAX sections being accurate that should help users self-correct.
The only other way I can think to do it would be to have external help files or placeholders in the comments that are rewritten as the module is loaded by using PowerShell to read and edit the files, but the effort required doesn't seem worthwhile.
Is there a way to find a list of script files that reference a given module (.psm1)? In other words, get all files that, in the script code, use at least 1 of the cmdlets defined in the module.
Obviously because of PowerShell 3.0 and above, most of my script files don't have an explicit Import-Module MODULE_NAME in the code somewhere, so I can't use that text to search on.
I know I can use Get-ChildItem -Path '...' -Recurse | Select-String 'TextToSearchFor' to search for a particular string inside of files, but that's not the same as searching for any reference to any cmdlet of a module. I could do a search for every single cmdlet in my module, but I was wondering if there is a better way.
Clarification: I'm only looking inside of a controlled environment where I have all the scripts in one file location.
Depending on the scenario, the callstack could be interesting to play around with. In that case you need to modify the functions which you want to find out about to gather information about the callstack at runtime and log it somewhere. Over time you might have enough logs to make some good assumptions.
function yourfunction {
$stack = Get-PSCallStack
if ($stack.Count -gt 1) {
$stack[1] # log this to a file or whatever you need
}
}
This might not work at all in your scenario, but I thought I throw it in there as an option.
Let's say I wrote a PowerShell script that includes this commmand:
Get-ChildItem -Recurse
But instead I wrote:
Get-ChildItem -Re
To save time. After some time passed and I upgraded my PowerShell version, Microsoft decided to add a parameter to Get-ChildItem called "-Return", that for example returns True or False depending if any items are found or not.
In that virtual scenario, do I have I to edit all my former scripts to ensure that the script will function as expected? I understand Microsoft's attempt to save my typing time, but this is my concern and therefore I will probably always try to write the complete parameter name.
Unless of course you know something I don't. Thank you for your insight!
This sounds more like a rant than a question, but to answer:
In that virtual scenario, do I have I to edit all my former scripts to ensure that the script will function as expected?
Yes!
You should always use the full parameter names in scripts (or any other snippet of reusable code).
Automatic resolution of partial parameter names, aliases and other shortcuts are great for convenience when using PowerShell interactively. It lets us fire up powershell.exe and do:
ls -re *.ps1|% FullName
when we want to find the path to all scripts in the profile. Great for exploration!
But if I were to incorporate that functionality into a script I would do:
Get-ChildItem -Path $Home -Filter *.ps1 -Recurse |Select-Object -ExpandProperty FullName
not just for the reasons you mentioned, but also for consistency and readability - if a colleague of mine comes along and maybe isn't familiar with the shortcuts I'm using, he'll still be able to discern the meaning and expected output from the pipeline.
Note: There are currently three open issues on GitHub to add warning rules for this in PSScriptAnalyzer - I'm sure the project maintainers would love a hand with this :-)
I want to use splatting syntax, but I want to do it inline
So instead of:
$p = #{Path = '.'}
ls #p
I want to do
ls ##{Path = '.'}
but this is a syntax error.
Is there anyway to splat a hashtable without having to write a separate variable?
Why do I want to do this? I prefer the hashtable syntax for defining alot of parameters (like 4 or more). But I'd rather not define a variable, I just want to have the cmdlet I'm calling at the "top" of the hashtable definition.
The splat operator will look for a variable whose name matches the characters after the splat sign, it doesn't try to resolve the characters after the sign as a statement to get the value. You cant do it inline. Link to TechNet.
As Francois mentioned, this is not possible yet, however, there are some discussions taking place around adding expanded splatting functionality (including this feature specifically) in the PowerShell RFC repository on GitHub:
RFC: https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0002-Generalized-Splatting.md
Discussion: https://github.com/PowerShell/PowerShell-RFC/issues/6
Consider sample powershell module sample.psm1 which exports single variable as follows:
$ROOT = "C:\root"
Export-ModuleMember -Variable ROOT
This can be imported specifying a prefix:
Import-Module .\sample.psm1 -Prefix "Sample::" -Force
Even though prefix works fine for referencing module functions (e.g. you can now do Sample::SomeFunction) it does not seem to work for variables, i.e.
$Sample::ROOT does not work, neither does
Sample::ROOT,
$(Sample::ROOT),
Sample::$ROOT,
they are imported and available through global names only ($ROOT in this case)
What are possible options for forcing a prefix on imported variables? What is a general best practice for dealing with imported variables?
manually prefixing module variables in module declarations could do, but it's kind of ugly approach to namespacing
Seems to me that generally you wouldn't want to export variables from a module, but instead provide getter/setter functions, for the same reasons that .NET classes don't usually use fields, but properties instead.
If you execute Import-Module with -Verbose on your module you'll see that functions are prefixed and variables aren't. I take that as a strong indication that prefixes on variables aren't supported.
However, you can use this code to access variables and functions in myModule:
$myMod = get-module myModule
& $myMod {$root}
This gives access to variables & functions in modules ahead of like named variables & functions outside of the module. (If you use that code to access a variable not in a module, but in an outer scope, you'll get the variable in the outer scope, because modules have access to variables in outer scopes.) By not exporting a module variable the only way to access it from outside the module is by qualifying it with $myMod.
So not quite a namespace but close.