© Adam Bertram 2020
A. BertramBuilding Better PowerShell Codehttps://doi.org/10.1007/978-1-4842-6388-4_10

10. Write for the Next Person

Adam Bertram1  
(1)
Evansville, IN, USA
 

Have you ever come across a script on the Internet that looks exactly like what you want but you can’t understand it? It has no comments, no help content, and you can’t follow the code? This situation is all too common. Don’t be that script author.

Write your code so the next person can understand your code. Writing clear, concise code also means you’ll end up helping yourself. More often than not it seems that after not touching a piece of code for six months or more, you are the “next person” who ends up having to come back through and clean up what you previously wrote. By taking the following precautions, you’ll be able to save yourself and the next coder countless hours on your projects.

Give Your Variables Meaningful Names

Variable names should be meaningful. When you go back through your code, you shouldn’t be wondering what $x is or how you are supposed to use it. You should be able to pick up exactly what it’s supposed to do the first time through. With PowerShell, there’s no practical limit to how long your variable names should be, so feel free to be as descriptive as possible.

Let’s say you have a list of server names in a text file. You’d like to write a script that reads this text file and connects to each of these servers. Technically, something like the following code snippet would work. PowerShell doesn’t care how you name variables but humans do!
$t = 'servers.txt'
$x = Get-Content -Path $t
foreach ($r in $x) {
    Invoke-Command -ComputerName $r -ScriptBlock {'Doing something on this computer'}
}
There is nothing syntactically wrong with the preceding snippet and, as is, something with intermediate PowerShell skills could probably read what it’s doing but with unnecessary difficulty. Compare that snippet with the following one:
$serversFilePath = 'servers.txt'
$serverNames = Get-Content -Path $serversFilePath
foreach ($serverName in $serverNames) {
    Invoke-Command -ComputerName $serverName -ScriptBlock {'Doing something on this computer'}
}
Notice how just a simple change of variable names makes the code so much clearer as to what’s going on. If, for example, you’re looking at the Invoke-Command reference, you don’t have to pain stakingly decipher the code like this:
  • Notice the $r variable

  • Look up to see that $r is the foreach loop iterator variable part of the $x collection

  • Continue to follow where $x came from to find that it’s file contents from some file pointing to $t

  • Then finally see that $t points to servers.txt

You could have saved four steps just by changing the $r variable name to what it actually is ($serverName).

Tip Source: https://twitter.com/lh_aranda

Further Learning

String Substitution

When working with strings, you sometimes need to insert variable values inside of other strings. One way that is a bit clearer than other methods is called string formatting using the -f string operator. The -f string operator acts as a placeholder allowing you to insert values inside of other strings.

Let’s say you have a script that provisions your company’s servers. You’re using the script to create a standardized naming convention with server names that start with the department name they’re being used for like HR-SRV, ACCT-SRV, and ENG-SRV.

You have all of the departments defined in an array like the following:
$departments = @(
    'HR'
    'ACCT'
    'ENG'
)
You then need to concatenate strings together to come up with each server name. You could merge the department name and the rest of the server name label using variable expansion as shown in the following:
foreach ($dept in $departments) {
    $srvName = "$dept-SRV"
    $srvName
}
Variable expansion inside of a string with double quotes works fine, but you can also use the -f format operator to remove the variable from inside of the string completely. The same functionality can be achieved using the following snippet:
foreach ($dept in $departments) {
    $srvName = '{0}-SRV' -f $dept
    $srvName
}

Now you have a placeholder ({0}) representing the place where the value of $dept will be. Most people consider string formatting a bit more clear especially when it comes to inserting many different variables inside of a single string.

Tip Source: https://twitter.com/harringg

Further Learning

Keep Aliases to the Console Only, Not in Scripts

PowerShell has way you can refer to commands via different names than their actual command name called aliases. Aliases are typically used to shorten the number of characters you have to type for a command. If you run Get-Alias, you’ll see all of the default aliases available to you. Even though you can save some keystrokes, there are a few major problems with aliases:
  1. 1.

    Lack of clarity You’re adding another layer of complexity by using a reference to a command rather than calling a command on its own.

     
  2. 2.

    Inconsistent syntax You are free to use aliases or the actual command name whenever you like. As humans, we are terribly inconsistent which probably means you’re going to use the command alias sometimes and the actual command other times.

     
  3. 3.

    Aliases can vary by system If you create a custom alias on your computer and then share that script with others, the script will break. You are including an unnecessary dependency in your script you will then have to manage.

     
  4. 4.

    Some aliases are not cross-platform – ls, for example, is an alias for the Get-ChildItem cmdlet on Windows, but ls is an actual Bash command on Linux. Invoking ls on Windows and Linux invoke different commands altogether.

     

Don’t use aliases in your code. Aliases are fine as shortcuts in your console session but keep them there. Don’t be tempted to use aliases in scripts or modules. Your code will be simpler and clearer.

Tip Source: https://twitter.com/TheTomLilly

Further Learning

Put Functions in Alphabetical Order in a Module

If you have a module file (PSM1) with many different functions, consider defining them in alphabetical order. If you have a module with dozens of functions, you should make it as easy as possible to find a function. One way to do that is through alphabetical ordering.

Alphabetical ordering will come in handy too when you’re working in an editor like Visual Studio (VS) Code. In many editors, you can bring up a list of all functions defined in a file. In VS Code, you can type Ctrl-Shift-O while you have a module file open and you’ll immediately see all of the functions defined in that file. If all functions were in alphabetical order, you can quickly scan through the list to find the function you’re looking for.
../images/501963_1_En_10_Chapter/501963_1_En_10_Fig1_HTML.jpg
Figure 10-1

Viewing functions in Visual Studio Code

Out of order module function listing in VS Code

Tip Source: https://twitter.com/raychatt

Explain Regular Expressions with Comments

There are times when you need to match and parse strings in PowerShell and simple pattern matching just won’t cut it. You must use regular expressions. If you have to write a complicated regular expression in a script, be sure to provide a comment above it indicating exactly what kind of string it matches. There aren’t many regular expression gurus out there that can read a regex string like reading command name. Make the code as easy to understand as possible.

Include some example strings the regex expression matches too. Examples are always appreciated.

For example, let’s say you need to match a globally unique ID (GUID). In the following you will see the regular expression to match a GUID. It’s not intuitive at all to say the least. No one can take a quick glance at that string and know its purpose is to match a GUID.
$regexString = '({){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}'
However, if you have a small comment above that string, it gives the reader an idea of what that regular expression’s purpose is.
## Matches all GUIDs
$regex = '({){0,1}[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(}){0,1}'

This is a simple tip but one that will help bring more clarity to those regular expressions.

Tip Source: https://twitter.com/guyrleech

Write Comment-Based Help

Always include comment-based help especially if you’re sharing scripts with other people. Comment-based help is the easiest kind of help to write. It shows up when a user runs Get-Help and acts as comments in the code too. Comment-based help should be standard across all production-ready scripts you write.

Anyone can write comment-based help for scripts or for your own functions. Maybe you have a function that reads a configuration file for some line-of-business application. You can create comment-based help as shown in the following to define what this function is doing and its purpose:
function Get-ConfigurationFile {
        <#
        .SYNOPSIS
            Finds the configuration file for the Acme application.
        .DESCRIPTION
            This function attempts to find the configuration file for Acme application
                based on the path provided. If found, it then returns a file object to then
                pass to other functions to manipulate the configuration file.
        .EXAMPLE
            PS C:> Get-ConfigurationFile -FilePath 'C:Program FilesAcmeAppconfig.xml'
                This example will look for the configuration file at C:Program FilesAcmeAppconfig.xml
                and, if found, will return a file object.
        .OUTPUTS
            System.IO.FileInfo
    #>
    param(
        [Parameter()]
        [string]$FilePath
    )
    ## Some code here that looks for the configuration file
}

Once this function is loaded into your session, you can then use Get-Help -Name Get-ConfigurationFile to see all of the help content without even going into the code.

Further Learning

Weigh the Difference Between Performance and Readability

When writing code, you’re forced to weigh decisions based on many factors. Two factors that sometimes collide are performance and readability. It’s important for code to accomplish a task as quickly as possible but not at the cost of readability. Even though a computer doesn’t need white space, comments and long command names doesn’t mean humans don’t.

There are many instances where you can increase performance in expense of readability, but let’s narrow down an example to a simple one: adding items to a collection.

You can add items to a collection many different ways in PowerShell. Some ways are simple and succinct yet others are faster by getting you into creating your own .NET objects and calling methods.

Let’s say you have a small collection of 100 items in an array that you’ve defined as the following. In this case, we’re using the range operator to quickly create an array of numbers zero to 100 for simplicity.
$array = 0..100
When you’re working with small arrays like this, you can add items to this array using the += operator as the following. You can see this method is simple, succinct, and pretty intuitive.
$array += 101

However, this simple method is actually tearing down that collection in the background and creating a new one. PowerShell is so fast though, you wouldn’t notice anyway for even collections with a few thousand items in it.

A faster, yet more complex, way to create an array and add items to it is to use an ArrayList as shown in the following. This method forces you to explicitly cast your basic array to a System.Collections.ArrayList type. Then, to add an item, you must call the Add() method.
$arrayList = [System.Collections.ArrayList](0..100)
$arrayList.Add(101)

This method is clearly more obtuse than the earlier example, but it’s faster especially with large collections.

Don’t use the fastest method all of the time if it’s going to affect readability. If you don’t ever intend to process more than a few thousand items in the collection, just use the += operator; otherwise, use an array list.

Further Learning

  • Consider Trade-offs Between Performance and Readability

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.118.95