Learn PowerShell by Rewriting VBScript

If you’re a busy admin and you are new to PowerShell, you might wonder just where to start.  Yes, you’ve downloaded PowerShell and perhaps played with it, trying a few lines of script.

But how can you learn all that from scratch?

If you know VBScript, why not convert a few short scripts to PowerShell?  Here’s one example, stolen from SAPIEN Technology’s blog:  Play WAVs with SAPI.

This script uses the Microsoft Speech API in an inventive way to play WAV files in the Windows\Media directory.  It first speaks the name of the file, then plays it. 

Here’s my version:

 

# speak-wav.ps1
function DemoWav {
param ($File)
trap {return}
    $objFile.Open($file.FullName)
    $objSPVoice.Speak($file.Name)
    $objSPVoice.SpeakStream($objFile)
    $objFile.Close()
return;
}

$objSPVoice = new-object -com "SAPI.SpVoice"
$objFile = new-object -com "SAPI.SpFileStream"

$strMediaDir = join-path $Env:Windir -childpath "Media"
get-childitem $strMediaDir -filter "*.wav" | foreach {
    write-output $_.name
    demowav $_
    }

Let’s deconstruct this:  PowerShell, unlike VBScript, requires you to declare your functions up-front before they’re called by the main code.  Function Demowav flows and performs the same way as in the VBScript version, so we’ll get back to that later.

Continuing on to the start of the main code,  almost all VB scripts except the very simplest access COM objects like so:

Set objSPVoice = CreateObject("SAPI.SPVoice")

In PowerShell this is:

 

$objSPVoice = new-object -com "SAPI.SpVoice"

This is one of the most common conversions you’ll see.  Remember that references to objects within VBScript are almost always COM or ActiveX so you need to specify the -com option in new-object or else PoweShell will think it’s a .Net object and error out.

Another place where the idioms of VBScript and PowerShell are very different is in file and directory processing.  VBScript uses a file system object at a very low level, so your script code has to do all the low-level work of traversing directories and resolving file paths, as in this code:

 

strWinDir= objShell.ExpandEnvironmentStrings("%WinDir%")
strMediaDir=strWinDir & "\Media"

Set objFSO=CreateObject("Scripting.FileSystemObject")
Set objFiles=objFSO.GetFolder(strMediaDir).Files

For Each file In objFiles
'only get .wav files
    If Right(file.name,3)="wav" Then 
    WScript.Echo file
    DemoWav file
    End If
Next

I am sad to recall that in the early days of Windows NT scripting, I was having to do stuff like this in Perl. Parsing a string to get the extension?  Ewww?  Going to yet another object to get the environmental variables? 

To get the %Windir% environment variable we simply do this in PS:

$Env:Windir

Here’s the main body of our code:

$strMediaDir = join-path $Env:Windir -childpath "Media"
get-childitem $strMediaDir -filter "*.wav" | foreach {
    write-output $_.name
    demowav $_
    }

Join-path simply takes two filenames and joins them in a path, so that $Env:Windir and "Media" become (when resolved) "C:\windows\media\".

get-childitem is the most important command:  This cmdlet, when applied to a filesystem, will return a list of objects corresponding to files in that directory.  The -filter parameter tells the cmdlet to filter the output as specified, so in this case, we are only returning *.WAV files.

Learn this cmdlet;  you’ll use it everywhere. 

The pipe "|" character is where the fun starts.  I mentioned that get-childitem returns a list of objects.  These objects get piped to the next statement:

| foreach {
    write-output $_.name
    demowav $_
    }

That’s our loop. It’s very similar to the VBScript.  Note the $_ variable.  You’ll see this everywhere.  That variable just means "current object in the pipeline".  So for each WAV file, the script writes the name of the file to the console with write-host and calls our function demowav.  Note that there are no parentheses in function calls in PowerShell, unlike VBScript and everywhere else.

Now we’re back to the function at the top of the file:

 

function DemoWav {
param ($File)
trap {return}
    $objFile.Open($file.FullName)
    $objSPVoice.Speak($file.Name)
    $objSPVoice.SpeakStream($objFile)
    $objFile.Close()
return;

This is straightfoward and almost identical to the VBScript code.  param just declares valid function and program parameters (remember, no parens for function declarations in PS!)  trap {return} just tells this function to return from the function in case of errors.  The only point of difference is that $file is an object, rather than just a string representing a file name, so we need to specify $file.FullName–which is the file’s complete path–when we open the file.  ($File.Name provides just the name without the full path.)

This was a long post, but if you got through it, and have basic knowledge of PowerShell, you’ll be able to convert short scripts nearly in your head.  It took me two hours to write this post but only 10 (!) minutes to co

nvert the original script. 

Microsoft’s Script Center has a guide on converting VBScript to PowerShell that you’ll find useful.

Have Fun!

Dave

del.icio.us tags:

Advertisements

Wither Vista?

Vista is out (for MSDN and Technet members, anyway), but for the first time since 1995, I’m not first in line.

In 1995, when I had only owned my first Windows machine for 3 months (RIP, WfW 3.11, I don’t really miss it), I got a beta copy of Windows 95 a month before its famous launch.

I skipped 98, SE and ME (with good reason) but I snapped up XP as soon as I could get an RC copy.  I rebuilt my machine with the new OS just as soon as it went gold.

Vista?

Not so much.  I tried Beta 2 under Virtual PC.  Um, ooookay.  That version was only good for its DVD case!  (I tried microwaving the DVDs only to find that pressed DVD’s don’t make impressive sparks, but only delaminate and melt quietly…)

RC1 was much better and RC2 better yet in a VM.  But I still haven’t run Vista outside of a VM.  I’m aware that much of Vista isn’t present under a VM (no 3D, no USB, etc.) and know I haven’t used it to its fullest.  I’d been hoping to run it full time when "one more program" is compatible, but with the news that PowerShell won’t be gold for Vista until its official launch date of January 30th, 2007, I don’t have a reason to switch yet.

As well, I’m not happy with the pricing for Vista Ultimate at $399.  Unless I get an OEM deal or get the upgrade version for $259 (I have a legal full non-OEM copy of XP), I’m inclined to drag it out especially as I need Office (before the 2007 beta, the version I used was Office97 :o.)

I’m not nostalgic about XP;  I see its flaws every day at home and work (no good native browser until IE7, 32-bit XP consumes resources too fast, removable USB drives a pain in the butt to actually disconnect from the system, and of course, security or lack thereof.)

(BTW, I need the features in Ultimate;  I produce video for fun and my home machine is part of a domain on my home SBS box.)

But I wouldn’t mind if there were a six-month decline in Vista Ultimate sales to make Microsoft reconsider, even if it’s to drop the price only slightly to bring it in line with the other releases.  Ideally I’d pay $100 for home or buslness and $200 for Ultimate.  I’d get started about Office pricing but that’s for another post.

Take care,

Dave

 

del.icio.us tags:


Powershell 1.0 is out–but only for XP and 2003!

PowerShell 1.0 is gold, but only for Windows XP and 2003.  There is a new version of PowerShell for Vista but only RC2. 

Get Windows PowerShell 1.0

 

del.icio.us tags: