My nickname in the networks is Coding Bugs, and I have been working as a software architect and developer from 2004. In my dashboard, you will find articles about programming languages, programming techniques, and other concepts that will help you improve.
You already know that PowerShell is the programming language that Microsoft makes available to us for "a cross-platform task automation solution composed of a command-line shell, a scripting language and a configuration management framework".
In this article I will show you a different way to provide parameters to scripts and commands. This is a simple and easy way of declaring parameters and assigning their values that will allow you to organize your scripts so that everything makes more sense.
Standard way
If you know PowerShell you already know how to provide parameters in your calls. If you don't, just type the name of the parameter preceded by a dash, then type its value.
$ Get-ChildItem -Path C:\ -Filter *.txt
# Returns all existing text files in C:
As you can see, the standard way is very easy and for cases where you make immediate invocations it is the best way to provide parameters.
However, imagine that you have to count the number of files of a certain type located in a specific place on your machine. In this case, we would have to repeat as many times as necessary the invocation of the above command, Get-ChildItem
, providing the Filter
parameter based on the requested file type.
$ Get-ChildItem -Path C:\ -Filter *.html | Measure | Select Count
# Returns all existing HTML files in C:
$ Get-ChildItem -Path C:\ -Filter *.css | Measure | Select Count
# Returns all existing CSS files in C:
$ Get-ChildItem -Path C:\ -Filter *.js | Measure | Select Count
# Returns all existing JS files in C:
To save us time each time we are asked for this information we are going to generate our own script in which we add these calls. In addition, we are going to incorporate a parameter in the script to specify the location where to count each one of the HTML, CSS and JS files.
param(
[Parameter(Mandatory=$true)]
[String] $Location
)
Return @{
Html = $(Get-ChildItem -Path $Location -Filter *.html | Measure).Count
Css = $(Get-ChildItem -Path $Location -Filter *.css | Measure).Count
Js = $(Get-ChildItem -Path $Location -Filter *.js | Measure).Count
}
What happens if we are now asked to include more and more file types with different extensions? The answer would be to include more and more lines to our script to fulfill the request. But what if there was another simpler and more versatile way, giving our script the ability to abstract from extension types?
A hashtable
for passing parameters
We will start by adding a new parameter indicating the extensions of the files we have to count. Now we will only have to iterate over these values and pass the corresponding extension to the Get-ChildItem
command.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower()
# Transform extension to lower case
Return @{ $_ = $(Get-ChildItem -Path $Location -Filter $CurrentExtension | Measure).Count }
# Return an object with the extension as the key and the count obtained as the value
}
This would be enough for our purpose but, as a programmer, we want it to be even more abstract and allow us to keep the Get-ChildItem
command call cleaner, avoiding having to modify it again with new parameters.
Being a somewhat complex command line, it is better to avoid modifying it as much as possible or to make it simpler by breaking it into simpler pieces. For the sake of this article, we choose the first option.
PowerShell allows the passing of a hashtable
object to provide each of the parameters we need from the command to be executed. In our case, the command is Get-ChildItem
and the parameters we are using are Path
and Filter
.
# The hashtable variable that we will create will look like the following one
$CmdParams = @{
Path = "."
Filter = "*.*"
}
# And the command call is like the following, important the use of "@" character
Get-ChildItem @CmdParams
As in the previous case, we have to update our hashtable
variable with the corresponding extension in each iteration of the Foreach
.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
$CmdParams = @{
Path = $Location
Filter = "*.*"
}
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower()
# Transform extension to lower case
$CmdParams.Filter = "*.$CurrentExtension"
# Set the filter to the current extension
Return @{ $_ = $(Get-ChildItem @CmdParams | Measure).Count }
# Return an object with the extension as the key and the count obtained as the value
}
We have achieved what we were looking for, we have abstracted the call to the Get-ChildItem
command with respect to the parameters we need to pass to it. This way, we can modify the CmdParams
variable by adding, changing or removing any key/value pair based on the functional needs we have.
We can see here the second principle of the SOLID paradigm, Open-Closed Principle, which states that the code should be closed to any modification but open to extend its functionality. We can say that the purpose of the iteration over each of the extensions is to return the number of existing files in the indicated location. The
Get-ChildItem
command can be invoked with other parameters that will modify the number of files returned but its result will remain the same (closed to modification), therefore, ourCmdParams
variable allows that the files to be returned can be different depending on the parameters received (open to extension).
Imagine that we are asked for another new functionality. Now we need to count the files in the location provided and the files in the folders immediately inside, i.e. the first level.
Let me explain that the Get-ChildItem
command has parameters that allow us to implement this type of requirements without the need to do any advanced engineering, we will use the Recurse
parameters to establish that the search is performed in the indicated location and in all the ones below and, Depth
to establish the maximum number of recursion that we need, in our case, the value is 1.
Surely you have already visualized the change to be made to the previous code. From my point of view, something very simple that will not take us more than 5 minutes between the change and the tests to verify that it is correct.
In our code we have to include the above two parameters in the CmdParams
variable for it to be applied. It is a clean change, just add new elements in our hashtable
without having to modify the command line where the command is located which, as we have said, is certainly complex.
param(
[Parameter(Mandatory=$true)]
[String] $Location,
[Parameter(Mandatory=$true)]
[String[]] $Extensions
)
$CmdParams = @{
Path = $Location
Filter = "*.*"
Recurse = $true
Depth = 1
}
Return $Extensions
| Foreach {
$CurrentExtension = $_.ToLower() # Transform extension to lower case
$CmdParams.Filter = "*.$CurrentExtension" # Set the filter to the current extension
Return @{ $_ = $(Get-ChildItem @CmdParams | Measure).Count } # Return an object with the extension as the key and the count obtained as the value
}
Summarizing
Passing parameters to PowerShell commands and scripts is simple. We can use the standard way for command calls or simple scripts. And, also, we can use more advanced techniques that allow us to have a cleaner code and implement modifications in a simpler way.
Using hashtables
is an easy way to visualize the parameter values of the commands we invoke. In addition, it structures our code making it more readable for others or even for ourselves after a while.
The code for this article can be found in this gist.
Feedback
If you liked this article, do not forget to click on the heart or the unicorn and share it with your friends and colleagues. Leave me your comments at the bottom about what you liked or how to improve and if you want me to write about other topics send me a direct message to my Twitter account.
Credits
The header image comes from Unsplash and is courtesy of Markus Spiske.
Top comments (0)