PowerShell How-To

Scrutinizing Old PowerShell Script

Periodically taking a look at your old work will give you an appreciation for how far you've come.

The kind of script you write today vs. years, months or even weeks ago is going to be different. Experience will greatly influence the way you go about solving problems and will manifest itself in the way your scripts are written. Chances are you will solve a problem today on a very different level that you faced a year ago. This is obvious, right? The more practice you have the better you get. There is no difference with your PowerShell scripts.

I've been writing PowerShell scripts regularly since late 2011. That means I'm coming up on my three-year anniversary being a PowerShell junkie. During this time I've accumulated hundreds of scripts along the way to solve just about every problem I've found. When you've accumulated that many scripts, inevitably you will come across scripts that you wrote when you were first playing around with PowerShell. Many years later you open one up and think, "What dufus wrote this script? It's terrible!" It then sets in that the "dufus" was actually you --only a three-year previous version of yourself. Talk about an ego letdown! Suddenly, you tend to have a little more compassion to those "newbs" in the PowerShell forum wanting help with the basics. That "newb" was once YOU!

When I'm alerted to how much I didn't know at one time I sometimes like to reflect on what I've learned over the years. Why did I write that code the way I did? What was I thinking? Personally, I can answer that question by saying it was what I was used to at the time. When I wrote the script I'm about to show to you I was writing a lot of PHP at the time. I could get PowerShell to work just by following the same principles I'd picked up in PHP (and some VBscript). The more experience you get with PowerShell the more you write code "the PowerShell way." You start to use the pipeline more, always outputting with objects, writing advanced functions, etc. It all comes with time.

I'd like to share an actual script I wrote, judging from the timestamp, on November 12, 2011. I once owned a book-selling business where I sold used books online. I had an inventory management software product I used to keep track of all my books. This software product allowed you to input multiple books at a time when imported via a CSV file. To make the creation of this CSV file easier I created a PowerShell script that prompted me for all the fields I needed to fill in and, when completed, would create a nicely-formatted CSV file ready to be imported into the application.

Let's break down this script and how I would write it now.

Then:

if (Test-Path 'new_pdps.tsv') {
ren 'new_pdps.tsv' 'new_pdps_renamed.tsv';
}##endif

Now:

if (Test-Path 'new_pdps.tsv') {
Rename-Item -Path 'new_pdps.tsv' -NewName 'new_pdps_renamed.tsv'
}

I now use the full cmdlet name of `Rename-Item` instead of using the alias. It's much easier for someone to understand your script if you don't use aliases. Next, I use path names instead of relying on positional parameters. By specifying the full parameter name and its argument again allows for explanatory code. You're simply being more specific and actually making your script's logic simpler in the process. I also removed the semicolon at the end. In PHP, that's necessary. In PowerShell, it's not.

Then:

$aHeaderRow = @('EAN','Weight','Title','Binding','Author','PubDate','Publisher','SeriesTitle','SeriesVolume','Edition');
$sRow = $aHeaderRow -join "`t";
Add-Content '.\new_pdps.tsv' $sRow

do {
$aPdp = @();
$aPdp += (Read-Host "ISBN");
$aPdp += (Read-Host "Weight");
$aPdp += (Read-Host "Title or Q to quit");
$aPdp += (Read-Host "Binding";
$aPdp += (Read-Host "Authors (comma-separated)");
$aPdp += (Read-Host "Publication Date");
$aPdp += (Read-Host "Publisher");
$aPdp += (Read-Host "Series Title");
$aPdp += (Read-Host "Series Volume #");
$aPdp += (Read-Host "Edition #");
$sPdp = $aPdp -join "`t";
$sPdp = $sPdp.Trim("`t");
Add-Content '.\new_pdps.tsv' $sPdp
} until ($sPdp -eq '');


Now:

function New-CsvFile ($Path = '.\new_pdps.tsv') {
$Header = New-Object –TypeName PSObject
@('EAN','Weight','Title','Binding','Author','PubDate','Publisher','SeriesTitle','SeriesVolume','Edition') | foreach {
$Header | Add-Member –MemberType NoteProperty –Name $_ –Value ''
}
$Header | Export-Csv -Path $Path -NoTypeInformation -Delimiter "`t`" }

New-CsvFile

This function would create a blank CSV file with the headers I need. After creating the CSV file I'd then open it up with Excel and fill in the values that way. It'd be quicker just to tab through all the fields instead of being prompted.

I majorly overhauled this section. I not only removed many lines of code, I essentially took away a lot of the script functions in lieu of just filling in data in a CSV file. This goes to show you that not all problems are solved with code.

In addition, using that same array of strings I had before I was then able to create a single object with each of those strings as properties. Once I had that object I could then pipe it to Export-Csv to create a nicely formatted CSV file with a header row. Notice how this saved a lot of code? Practice the DRY principle (Don't Repeat Yourself). If you find yourself typing the same set of character over and over again you're probably adding unnecessary code.

It's always a good idea to use objects whenever you can in PowerShell. They are very powerful! In this case using objects allowed me to be more accurate with creating the fields. It's not a good idea to try to create structured data like a CSV file with Add-Content. Add-Content is meant to write misc lines to text files. It's not meant as a means to record plaintext, structured data like CSV, XML, etc. Objects are there to gather the info and Export-Csv is there to accurately output to a CSV.

Finally, although a personal preference is that I no longer use hungarian notation. Notice the 'a' and the 's' as the first character in the variable names? I used to do this to signify what data type the variable was. You could still do this but with PowerShell a variable type is just a `Get-Member` away!

 

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular