Whenever I review PowerShell scripts, I usually consider loops (especially nested ones) to be a code smell.

It usually indicates to me that script is unnecessarily procedural and could benefit from being rewritten in a more functional manner.

Take this simple code snippet as an example. It searches sql files for a particular string (the poor man’s equivalent of ReSharper’s “Find Usages” in the sql world).

   1: $files = (Get-ChildItem -path $rootPath -include *.sql -recurse | sort FullName);
   2: foreach ($file in $files)
   3: {
   4:     $SqlFileContents = Get-Content $file;
   5:     $SqlFileString = "";
   6:     foreach ($item in $SqlFileContents)
   7:     {
   8:          $SqlFileString = $SqlFileString + " " + $item.ToString();
   9:     }
  10:
  11:     if($SqlFileString.Contains($searchText) -eq $True)
  12:     {
  13:         Write-Host "Reference found in : $file";
  14:     }
  15: }

Now consider this revised version, which takes the more functional approach of utilizing the pipeline to carry out a series of operations on a collection of files.

   1: Get-ChildItem -path $destinationPath -include *.sql -recurse | where{([string](Get-Content $_)).Contains($searchText) -eq $True} | sort FullName | select FullName;

I’m don’t use PowerShell frequently enough to feel confident that I can figure out the most elegant way to solve a problem, but the second approach seems like a huge improvement to me. It’s more concise and arguably even more readable.

So the next time you catch yourself writing loops in PowerShell (or C# for that matter), take a step back and try a more functional approach on for size.

UPDATE: Neil Barnwell reminded me in a comment about the Select-String cmdlet, which not only greatly simplifies the script but also adds the extra functionality of displaying line numbers and text excerpts in the results.

   1: Get-ChildItem -path $destinationPath -filter *.sql -recurse | select-string $searchText

I feel stupid for having forgot about the cmdlet, but at the same time it was fortuitous because otherwise I probably would have had to spend a lot more time coming up with a better example to make the same point for this post…:-)

Popularity: 2% [?]