Tuesday, March 31, 2009

My First PowerShell Script

For a while now, I've been planning on getting my hands dirty using  PowerShell. There are at least four features that make this a pretty compelling scripting environment:

  • The ability to write scripts combining regular scripting commands  with the full power of the .net base class library (also including Cmdlets that you implement youself).
  • The ability to easily define functions as a part of the script.
  • The Cmdlet Verb-Noun naming convention, giving the scripting syntax a consistent and easy to discover feel that is completely missing in the jungle of cryptic abbreviated commands of the scripting environments of yore.
  • Everything is object based, so that when you, for instance, loop through all files in a directory by using the built in Get-ChildItem function, you are in fact accessing objects that represent the files and not just a textual path.

I hereby announce that I have completed my first PowerShell script (see below).

The good thing about being a latecomer is that you get to use the newest version, so I went directly for PowerShell 2.0 CTP3. A very nice thing about this version is that it comes complete with its own Integrated Scripting Environment, and this has been tremendously helpful in the process of understanding the basics and weeding out bugs.

So, what does my very first PowerShell script do? It recursively copies the contents of one directory to another one. As input to this process it takes a list of directory matching filters and a similar list of file extensions to be used as a filter. All in all this serves the purpose of copying the entire contents of a directory will keeping away from certain paths and files as dictated by the filters.

So by giving in this: "_ReSharper","\obj","\bin", "\.svn" as directory filter and this: ".user", ".suo", ".resharper" as file extension filter, I get functionality for copying .NET source code directories without also copying all the crud that is lying around as a by-product of the VS build process, SubVersion, ReSharper and so on.

I guess that everyone with some PowerShell experience will view this script as childishly amateurish, but at least it works .

Your welcome!


function Passes-Filter($dir, $filters) {
foreach($filter in $filters){
if($dir.Contains($filter)) {
return ''
}
}
return 'True'
}

## Checks to see whether the extension of the
## file matches any of the filters. If so
## returns false, else true
function Passes-FileFilter($file, $filters){
$ext = [System.IO.Path]::GetExtension($file).ToLower()
foreach($filter in $filters){
if ($filter.Equals($ext)){
return ''
}
}
return 'True'
}

function Get-DestinationPath($basedir, $candidate, $destdir){
$baseLength = [int]$basedir.Length
$candidateLength = [int]$candidate.Length
if ($candidateLength.Equals($baseLength)){
return ''
}
else {
#Write-Host -ForegroundColor GREEN $candidate
$rightSide = $candidate.Substring($baseLength, ($candidateLength - $baseLength))
$dir = $destdir + $rightSide
return $dir
}
}

function Copy-CodeFile($basedir, $candidate, $destdir) {
$newFile = Get-DestinationPath $basedir $candidate.FullName $destdir
copy $candidate.FullName $newFile
}

function Make-CodeDirectory($basedir, $candidate, $destdir){
$newDir = Get-DestinationPath $basedir $candidate $destdir
if ([System.String]::IsNullOrEmpty($newDir)){
}
else {
mkdir $newDir
}
}

function Traverse-Directory($basedir, $destdir, $dir, $dirfilters, $filefilters) {
# #Write-Host 'About to traverse dir: ' $dir
foreach($candidate in Get-ChildItem $dir) {
if ([System.IO.File]::Exists($candidate.FullName)){
# It's a file
if(Passes-FileFilter $candidate $filefilters) {
Copy-CodeFile $basedir $candidate $destdir
}
}
else {
# It's a directory
if (Passes-Filter $candidate.FullName $dirfilters){
Write-Host -ForegroundColor GREEN $candidate
Make-CodeDirectory $basedir $candidate.FullName $destdir
Traverse-Directory $basedir $destdir $candidate.FullName $dirfilters $filefilters
}
else {
Write-Host -ForegroundColor RED "Stopped in dir filter: " $candidate.FullName
}
}
}
}

## Script entry point
Clear-Host
$dirfilters ="_ReSharper","\obj","\bin", "\.svn"
$filefilters = ".user", ".suo", ".resharper"
$sourceDir = 'C:\depot\MyProject\trunk'
$destDir = 'C:\temp\CopyOfMyProject'
Traverse-Directory $sourceDir $destDir $sourceDir $dirfilters $filefilters

1 comment:

  1. Isn't this an 3 page implementation of xcopy?

    ReplyDelete