I have a large script which I'm now cleaning up with the use of functions. But, I've found that it gets kinda messy to put them all at the top of the script.. I have to scroll past hundreds of lines of codes to get to the script itself..
How do you guys keep your scripts tidy? Do you have your functions in a separate file?
What to Do: Create multiple folders for each different module or set of scripts under the same project. Splitting these into their own folders means they can be built and versioned separately from one another. Possible Issues: The problem with project organization is it's difficult to organize at a very granular level.
PowerShell is a script, not a compiled language. Therefore, it goes through the script line-by-line, top to bottom, (after tokenizing the script) and evaluates each command along the way.
A function in PowerShell is declared with the function keyword followed by the function name and then an open and closing curly brace. The code that the function will execute is contained within those curly braces. The function shown is a simple example that returns the version of PowerShell.
Open a PowerShell console session, type exit , and press the Enter key. The PowerShell console will immediately close. This keyword can also exit a script rather than the console session.
If you use PowerGUI script editor you can use regions like this:
#region Set of functions A
function foo {
Write-Host "Just a function"
}
function bar {
return "Just another function"
}
#endregion
When you open the script in PowerGUI script editor the regions will be collapsed so you don't have to scroll to get to the main logic. This also works in Microsoft ISE. Not all script editors honor the region tags though.
Another way is to externalize your functions either into another script and do what is called dot sourcing . C:\myfunctions.ps1
or put them in a module file named with a .psm1
extension and use Import-Module
.
The technique that we have adopted is to make all of our functions loaded via a script module. We created a folder to hold all of the individual function files and further subdivided them into their appropriate categories. Once we have done that we create a .psm1
file to tell the module what to load and then add the module path to our PowerShell profile (if not in the default Module location).
Module-Name\
Subfolder1\
Subfolder2\
...
Module-Name.psm1
Module-Name.psm1 (located underneath a folder with the same name - Required)
# Script Module for Company Functions
Function Get-ScriptDirectory {
# $MyInvocation is an Automatic variable that contains runtime details and
# we can use this to get information about where the file is run from.
$Invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $Invocation.MyCommand.Path
}
Get-ChildItem (Get-ScriptDirectory) -Recurse `
| Where-Object { $_.Name -like "func_*" } `
| %{
. $_.FullName
}
Microsoft.PowershellISE_profile.ps1 / Microsoft.Powershell_profile.ps1
$LocalLibraries = "C:\Local\Path\On\Disk\"
$env:PSModulePath = $env:PSModulePath + ";$LocalLibraries"
The above code means that you don't have to store the module in the same location as the rest (useful in our case since we use SVN to version and share our stuff with our team).
To recap:
We prefix the "ps1" files with "func_" so that when tab-completing the function name, if in the same directory as the file, it will not get confused. In addition, we add the Namespace (company initials, etc) in front so that our function names will not collide with any other added functions.
One additional, useful tip, that might come in handy during development is to define an alias, "reload" in our case, that will force the module to reload. This means that once you have changed a file, all you have to do is type that and it will be dot-sourced into memory again with your changes.
# Function to reload Module
Function int_ModuleNameModuleLoad {
Import-Module Module-Name -Force -WarningAction SilentlyContinue
Write-Host "Module-Name Reloaded"
}
# Set Aliases
If (-not(Get-Alias "reload" -ErrorAction SilentlyContinue)) {
New-Alias -Name reload -Value int_ModuleNameModuleLoad -Force
}
The reason I use "int_" instead of our normal naming structure is that, this function is located within our profile and I consider it internal and not a full blown function.
I hope this gives you some good ideas, it has worked out great for us so far!
-Adam
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With