One of the funny things about PowerShell is the relationship between functions, scripts, and modules, and the resulting myriad of ways you have to organize a collection of functions. Users occasionally ask what the best way to do this is, and I generally answer that there’s no single best method — different users and even different machines will call for different solutions.
There was a post on the PowerShell Community Forums recently asking the question again, and I’m writing this partly to give my usual answer (with a little bit of insight), and partly to try to elicit responses from the broader PowerShell community:
How do you organize your functions?
There are several points of differentiation here: how you store them, how you organize them, and how you load them.
Scripts vs. Functions
Lets start with the question of how you store your functions. When you write a function, you have two basic choices of how to store it in a script file: with, and without the function FunctionName { line. For example, I wrote a function Get-GUID and saved it as Get-GUID.ps1:
foreach($null in 1..$count) {
"{{{0}}}" -f [system.guid]::newguid().ToString()
}
Because it’s stored without a leading line: function Get-GUID { (and matching closing brace } at the bottom), this script can be called directly: .\Get-Guid.ps1 -Count 5. However, if I put that file in my path, I can leave off the path (and I can always leave off the file extension, unless there’s another PowerShell executable file with the same name): Get-GUID 5.
If I added the leading function Get-GUID { line, I would have to FIRST dot source the script (essentially, running it’s contents in the current context to export the function out of the script file) . .\Get-GUID.ps1 and then call the function: Get-GUID 5.
Hopefully the trade-off here is obvious to you: the script takes up zero space in memory if you don’t use it, but must be loaded and parsed each time it is used. The function has to be pre-loaded, and takes up space in RAM, but is already pre-parsed (somewhat) and will run faster when needed. How much faster? That completely depends on the speed of your computer and the size of the script, but generally speaking, fractions of a second.
For example: WPK preloads all of it’s functions, while PowerBoots does not — other than that, they’re very similar, and they each have around 600 functions the way I have them set up on my computer. So WPK takes approximately twice as long to load as PowerBoots on my desktop (Core 2 Duo e6400): 3 seconds. WPK’s preloading uses about 150MB of RAM as soon as it’s loaded (vs 6MB for PowerBoots), and that number is the same on my desktop —where I have 8GB of RAM— as it is on my laptop where I only have 2GB.
Which is better? Well, that depends on how much free RAM you have (and are willing to give PowerShell), how fast your CPU is, and how often you’re going to use the functions. There’s just no clean answer (although I’d venture to say if you have lots of RAM and a weak CPU, or vice versa, you might have a clear-cut answer).
Scripts vs. Modules
The remaining two points (storage and loading) get mixed up together in my analysis, and they’re somewhat hard to separate.
When I was using PowerShell 1.0, I kept all my functions organized one-per-script (except in a few cases where I was sure I wanted to pre-load the functions, and the functions were directly related to each other). I put these scripts into folders inside a root “Scripts” folder in my PowerShell directory. Then, in my profile script, I would add all of those locations to my PATH environment variable, so I could dot source them or run them without typing the full path.
However, now that PowerShell 2 has brought us script modules, I’ve moved a lot of those folders from the Scripts folder into the new Modules folder, and added a simple .psm1 script file which adds that folder to my $ENV:Path (or dot sources them all). However, I haven’t done that with all of my script folders: only with scripts which I either want to preload when I’m going to use them (that is, ones which have function name { lines in them), or where I have a set of functions that are logically related. This not only keeps them organized in subfolders as before, but also keeps them from showing up in my Get-Command output when I’m not using them, and keeps them organized (into modules) after I import them.
Functions which are rarely used and aren’t part of a collection of scripts, or which take a lot longer to run than to parse, I have tended to leave in my script\subfolder organization system, but that’s just me: I haven’t seen the value of having a catch-all “utilities” module to import those, nor of moving them into their own module subfolders so they can be loaded via Import-Module. A few of the biggest ones, I have designed such that running them the first time exports the function to the global scope and then invokes it also so that subsequent calls run much faster (in fact, all the PowerBoots functions work this way) ... this gives me what I feel is the best balance of speed and memory use.
Modules are Vital
Nowadays, most of my modules are script modules where I have placed a series of functions all into a single psm1 file, which I then load when I need it, via Import-Module — in this case, Import-Module functions almost exactly the same as dot sourcing, with the exception that it keeps track of which functions were imported from which module, giving me that nice organized feeling, and keeping variables private in the modules when possible.
One of the other major features of modules is that you can define dependencies between them, so when you want to use a function, you don’t have to remember which other functions it’s dependent on… you just have to remember what module it’s in, and you can use the module metadata file to keep track of other modules it depends on.
Preloading or not?
The final point I want to make here is that apart from organizing your scripts into modules, you really don’t need to load all your modules every day. You should only pre-import modules that contain functions you’re actually going to use daily — load the others on-demand when you’re ready.
Keeping the number of modules that you pre-import down will keep your PowerShell startup times lower and keep PowerShell’s memory use lower as well.
Similar Posts:
- None Found
yay, you’re blogging again!
Take a look at “auto-loaded function” technique:
http://nightroman.spaces.live.com/blog/cns!F011223B604739FA!209.entry
Excellent
that’s exactly what I do, except that I repeat the PARAM blocks inside the function