PowerShell: best way to ensure function name uniqueness? - powershell

What's the best way to ensure your PowerShell function name is unique? The standard since version 1 is to put in a short unique id after the verb dash and before the noun. For example, with my initials I could create function Get-DWServer; this is fine until someone creates a function in a different module for getting an object reference to a datawarehouse and uses the same function name. Two or three letters just isn't sufficient but more than that gets ugly to read.
I'd prefer to have a unique prefix* similar to .NET namespaces. It's better for organization, easier on the eye and works with tab completion. And it expands gracefully so you could name it DW.Get-Server or DW.Network.Get-Server.
The downside of doing this is it runs afoul of PowerShell's proper verb check during module import/Export-ModuleMember. You can get around this by specifying DisableNameChecking during import but I'm wondering if doing this is sloppy and might be bad if PowerShell 3 comes out with a better solution. I know PS verb purists (are there any?) will complain that this probably 'hinders discovery' but I can't think of a better option.
What do you do?
(*You can refer to an exported function using module_name\function_name notation but this won't work with tab completion and still doesn't get around the problem of the function name being unique).

I have heard Jeffrey Snover (the inventor of PowerShell) talk about this a few times and he described it as a dilemma, not a problem. A dilemma has to be managed but can't be solved completely. A problem can be solved. As a PS verb "purist" I would say the best way to manage this is to have a 2 or 3 letter prefix to your nouns. This has been sufficient so far for many widely distributed sets of cmdlets. IE, Quest AD Cmdlets vs Microsoft's AD Cmdlets. Get-ADUser and get-qaduser.
If you are consuming a module and want to use your own prefix, you can specify one with
import-module mymodule -Prefix myPrefix
I know this isn't the one single silver bullet answer, but I would say it works for 95% of the situations.

Related

Making all PowerShell script variables explicitly Typed

I'm new to PowerShell scripting and am looking to create ps1 scripts that I can used as cmdlets. My background is in using strongly typed variables, but I'm struggling to find how (or if) it is possible to ensure that all user variables in a script are explicitly typed. Some languages only allow explicitly typed variables. VBA allows the directive "Option Explicit" and I was hoping to find some way to achieve the same in any PSH scripts I create.
I've done a lot of searching (google, stackoverflow etc.) but not found anything. If there is no way to force all variable definitions to be explicitly typed, I'll have to write a cmdlet to parse my scripts to find any implicitly typed variables ... but hoping for a better solution.
I don't think you can do that in PowerShell. Closest you can get is to use Set-StrictMode which will, among other things, prohibit use of uninitialized variables.
But if you want to parse the scripts, maybe don't write your own solution. Use PSScriptAnalyzer module. It has a lot of built-in rules, unfortunately none for checking explicit types. But you can define your own rules, and maybe someone already created the one you're looking for and posted it somewhere.

In what scenario was Invoke-Expression designed to be used?

9/10 times if you are trying to use the Invoke-Expression cmdlet, there is a better way. Building the arguments to a command dynamically? Use an array of arguments. Building the arguments to a cmdlet? Use splatting with an array or hashtable. Your command has a space in the path to it? Use the call operator (&).
This might seem open ended, but Invoke-Expression is an easily accessible cmdlet where the answer is almost always to never use it. But the cmdlet exists for a reason, is not deprecated, and most criticisms of its use state something similar to, "it's almost never the right answer", but never states when it is acceptable to use it. In what case is it acceptable to use Invoke-Expression? Or to word it a bit less openly, how was Invoke-Expression designed to be used?
To quote from a PowerShell team blog post titled Invoke-Expression considered harmful (emphasis added):
The bottom line: Invoke-Expression is a powerful and useful command for some scenarios such as creating new scripts at runtime, but in general, if you find yourself using Invoke-Expression, you should ask yourself, or maybe a respected colleague if there is a better way.
EBGreen notes:
Or to phrase it another way, It [Invoke-Expression] is ok to use as long as a user is never involved in any part of generating the string that will be invoked. But even then, not using it will enforce better habits than using it would.
In short:
As a matter of habit, always consider a different (usually more robust and secure) solution first.
If you do find that Invoke-Expression is your only choice, carefully consider the security implications: if a string from an (untrusted) outside source (e.g., user input) is passed directly to Invoke-Expression, arbitrary commands may be executed.
Therefore: Only use Invoke-Expression if you fully control or implicitly trust the input.
Note: As of Windows PowerShell v5.1 / PowerShell Core v6.1.0, the official Invoke-Expression help topic doesn't provide such guidance; this GitHub issue suggests rectifying that.
Rare examples of justified (safe) use of Invoke-Expression:
Creating PSv5+ custom classes dynamically:
so that the class can be used in a remote session.
so that the set of properties can be created based on conditions at runtime.
Using Invoke-Expression in combination with Write-Output:
to parse a string with embedded quoting, with extra precautions.
to parse command lines stored in a file, if trusted.
Using Invoke-Expression for nested property access:
via a property path stored in a string.

What the Cmdlet Development Guidelines say about case use for functions in Cmdlets?

While I did find a lot of information on how to name Cmdlets and functions in the Cmdlet Development Guidelines I did not find any information on whether functions should be named in upper or in lower case.
What is the convention here?
(I do understand that Cmdlets themselves are generally named in upper case, even though they are not case-sensitive when it comes to executing.)
Naming convention can be tricky. While a fixed naming convention may provide aesthetics or simplified usage, it is not required to be followed. In general, a naming convention that I advocate for is the one that is used already in Powershell. As functions are created on verb-noun base, each word starts with a capital letter or if it is an abbreviation - all capitals, or if it is a proprietary - then as it is accordingly.
I have, for example, created some functions for myself:
Get-ServerDiag
Mount-TrueCryptVolumes
Start-RDP
Generate-RandomPassword
Nuke-Environment
You can imagine what these functions do, it is rather clear, straightforward and compliant with built-in Powershell functions. I do however have exceptions which come from "importing" a several Unix commands to Powershell (like killall, pidof etc...) You can always use a Set-Alias if you prefer to write something else.
This question, however important, is discussable as there does not seem to be the 'one, best way'. It is all, in the end, up to personal preferences.
A function is mostly a script-based cmdlet. A cmdlet is written in ex. C#, while a function is written as a script. Because of this similarity, I recommend using the same style as cmdlets for my "standalone-functions" so that it blends in with the other PowerShell cmdlets. Ex. I had a filecount(per folder)-function that I used often. I called it Get-FileCount.
However, I usually name helper functions(functions you only use in other functions) using a simpler name like convertsidtousername etc.
You could use aliases to create short names for a function.

Alternative to "Download" as a PowerShell verb?

I've got a PowerShell function Download-File, which uses WebClient.DownloadFile -- hence the name.
When I attempt to turn my .ps1 script into a .psm1 module, PowerShell warns me that "Download" is not in the list of recommended verbs.
What's a good alternative? Get- seems to be about getting properties, rather than contents (apart from Get-Content, oddly). Receive- seems a bit too passive for my liking (i.e. the script blocks until the information is sent) -- which doesn't fit well.
Ideas?
For me, 'Get' is the most natural verb. It gets everthing, not just properties. For the noun I would use something like 'WebFile', you can easily guess what you get and where it is coming from.
Wouldn't this work:
Start-Download -Url http://blah/
Or use Invoke-Download, as per the recommendations for synchronous operations. Start is for asynchronous.
What about New-Download or New-WebDownload or New-DownloadFile??
Maybe Request-TeamCityArtifacts?
https://learn.microsoft.com/en-us/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands?view=powershell-7.2
There is no synonym for download.
Copy is the closest I've found, being documented as a synonym of the unapproved Clone.

What is a good method for inventing a command name?

We're struggling to come up with a command name for our all purpose "developer helper" tool, which we are using on our project. It's like a wrapper for our existing tools like cmake and hg. The purpose of the command is really just to make our lives easier by combining multiple commands into one (for example, publishing packages). For example, we have commands like:
do conf
do build
do install
do publish
We've considered a few ambiguous names like do (as above) and run, but obviously, do is a Linux bash command and run is pretty ambiguous.
We'd like our command to be 2 chars short, preferably - but who thinks we're asking the impossible? Is there a practical way to check the availability of command names (other than just typing them into your terminal), or is it just a case of choose one and hope nobody else will use it? Are we worrying about nothing?
Since it's a "developer helper" tool why not use hm [run|build|port|deploy|test], Help Me ...
Give it a verbose name, then let everyone alias it to whatever they want. Make sure you use the verbose name in other scripts so that it removes ambiguity.
This way, each user gets to use whatever makes sense to him/her, and the scripts are more readable and more easily searchable (for example, grepping four "our_cool_tool" will usually yield better results than grepping for "run").
How many 2-character words are useful in this context? I think you need four. With that in mind, here are some suggestions.
omni
torq
fluf
mega
spif
crnk
splt
argh
quat
drul
scud
prun
sqat
zoom
sizl
I have more if you need them.
Pick one: http://en.wikipedia.org/wiki/List_of_all_two-letter_combinations
To check the availability of command names, I suggest looking for all two-letter filenames that are in the directories in your path. You can use a script like this
for item in `echo $PATH | sed 's/:/ /g'` ; do
ls -1d $item/??
done
It won't show builtins in your shell (like "do" as you mentioned) but it's a good start.
Change ?? to ??? for three-letter files, etc.
I'm going to vote for qp (quick package?) since it's easy to pronounce, easy to type, and easy to remember where the keys are on the keyboard.
I use "asd". it's short and most developers type it without thinking
(oh, and you can always claim later that it stands for some "Advanced Script for Developers" if you need to justify yourself a few years from now)
How about fu? As in Kung Fu. It's a special purpose tool. And it's really easy to type.
I think that run is a good name, at least anybody that will download your project will know what to do. Calling it without parameters should reveal your options.
Even 'do' will do, I think you can use backquotes to run it from bash scripts.
Also remember that running the tools without parameters will tell you what options you have.
Use makefiles to do everything for you.
How about calling it something descriptive, like 'build_runner', and then just aliasing it to 'br' (or preferred acronym) in your .bashrc?
There is a really crappy tool called cleartool (part of clearcase), and people will alias it on their machine to "ct". Perhaps you can have a longer command and suggest users alias it.
It would probably be best to do something like ire_and_curses suggested, name it descriptively then alias it to a 2 letter command. If I was choosing, I would name it dev_help and alias it to dh.
I think you're worrying about nothing. Install the program as 'the-command-to-do-evertyhing-and-if-you-dont-make-your-own-alias-for-it-you-should'. I don't think that will be too long for any modern filesystems, but you might need to shorten it to 'tctdeaiydmyoafiys'. See what common aliases are used, and then change the program's name to that. In other words: don't decide, let natural selection decide for you. If you are working with a team of < 10, this should not even remotely cause any problems.
Call it devtool alias to dt
Custom tools like that I like to start with the prefix 'jj-'. I can type (with big index-finger power) 'jj ' and see all my personal commands. Also, they group together in alphabetical lists. 'J' is not a very common character for built-inc commands, but you can pick your own.
Since you want two characters, you can use just 'zz', or something starting with 'z'.
Are you sure you want to put all your functionality in one command? That might be simultaneously over-constraining and over-loading the interface a little.
do conf
do build
do install
do publish