Hey, Scripting Guy!Where Are the Cup Holders?

The Microsoft Scripting Guys

This column is based on a prerelease version of Windows PowerShell. All information herein is subject to change.

Whenever a new technology is introduced it usually takes a while before people can really differentiate the new technology from whatever came before. Take the first automobiles, for example. There's a reason why those contraptions were known as horseless carriages: that's pretty much all they were. When you got right down to it, about the only difference between the first automobiles and horse-drawn carriages was the fact that one used horses and the other didn't. Other than that, it was hard to tell them apart.

Today, of course, few people would mistake an automobile for a horse-drawn carriage. That's because, over time, auto makers have managed to do things that clearly separate cars from carriages. For example, today's cars come straight from the factory replete with CD players, air-conditioning, and satellite navigation systems; few horse-drawn carriages can say the same. Likewise, suppose you were driving a horse-drawn carriage in the 1880s and needed to set down your Frappuccino for a moment; sorry, you're out of luck. By contrast, some SUVs come standard with as many as 17 cup holders, while there's at least one make of automobile that includes three cup holders for the driver alone. Some of the newer models even have cup holders that can heat or chill your beverage as needed.

Note Yes, it is hard to believe that people once had to survive without being able to drink three different beverages while driving. How are you supposed to drive with just one or two beverages at your disposal?

Before a new technology can really succeed people need to know why they should favor the new and unknown over the old and reliable. When DVDs first came out they allowed you to watch movies at home, just like video cassette recorders did. On the other hand, DVDs were way more expensive than video cassettes, and you couldn't use a DVD player to record your own shows. Needless to say, DVDs didn't exactly take the world by storm. In fact, it wasn't until DVD manufacturers started packing their discs with deleted scenes, commentary from an actor you didn't even know was in the movie, and other things you couldn't get on a cassette tape, that DVDs began to supplant their VHS counterparts. Being able to watch a movie at home wasn't enough; DVD makers had to provide capabilities above and beyond those capabilities people already had.

Note Whether or not anyone actually watches any of these bonus items is beside the point: human nature being what it is, if something is available then you simply have to have it. Coincidentally, the Scripting Guys are thinking about coming out with a DVD consisting primarily of deleted text from TechNet Magazine columns and alternate endings to system administration scripts.

Using Windows PowerShell

Windows PowerShell™, the new command-shell/scripting technology released by Microsoft, represents another example of this. When people first hear about Windows PowerShell they typically hear things like this:

  • "Windows PowerShell lets you manage processes and services."
  • "Windows PowerShell lets you retrieve events from your event logs."
  • "Windows PowerShell lets you use WMI to manage computers."

Now, there's nothing wrong with any of that; those are all useful and important tasks. The only problem, of course, is that you can already carry out all those tasks using VBScript and Windows Script Host. Backward compatibility is important, and Windows PowerShell wouldn't be much of an innovation if it couldn't even do the same things VBScript can do. (Imagine a DVD player that couldn't actually show movies. Actually, you don't need to imagine that: one of the Scripting Guys has one at home.)

At the same time, however, if Windows PowerShell can only do the same things that VBScript can, well, then why should we bother with this new technology in the first place? Admittedly, VBScript doesn't ship with any cup holders; then again, neither does Windows PowerShell. So what difference does it make which scripting technology we use?

To be honest, it might not make any difference whatsoever. On the other hand, lack of cup holders aside, Windows PowerShell does have some capabilities that VBScript does not. If all you need to do is manage processes and services then there might not be any compelling reason for you to look at Windows PowerShell; as the saying goes, if it ain't broke, don't fix it. (When it comes to the Scripting Guys this maxim tends to read "Don't you guys try to fix it because then it will be broke.") Where Windows PowerShell becomes truly attractive is when you can use it to do something that VBScript can't do. Something like, say, changing the last write time for a file.

Changing the Last Write Time

You heard right: changing the last write time for a file. For various reasons (such as a quick-and-easy form of version control) script writers have always dreamt of the day when they could change the last write time (or the creation time) for a file or set of files. Sadly, though, this capability does not exist in either Windows Management Instrumentation (WMI) or FileSystemObject; in both of those technologies, file dates and times are read-only.

With Windows PowerShell, however, changing the last write time of a file to the current date and time is as easy as running this command:

$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = Date

Before we explain how this command works, let's answer a question that's likely on everyone's mind: how can Windows PowerShell modify the last write time of a file when WMI can't? We thought we should address that question before doing anything else simply because it represents a common misconception regarding Windows PowerShell. People tend to assume that Windows PowerShell is just a new way to use WMI. Granted, you can definitely use Windows PowerShell to access WMI data; it's also true that, while we wait for additional cmdlets (native Windows PowerShell commands) to be constructed, most system administration scripts will continue to rely primarily on WMI. At heart, though, Windows PowerShell is based on the .NET Framework, and Windows PowerShell cmdlets typically deal with .NET objects rather than WMI objects. And that's important, because the properties available through the .NET Framework are not necessarily the same as the properties available through WMI.

For example, when we use the Get-Item cmdlet to bind to a file, we don't get back an instance of the WMI CIM_Datafile class; instead, we get back a .NET object. Is that really that big of a deal? You bet it is—the .NET object exposes a slightly different set of properties and methods for file management than WMI does. But, as we've just seen, those slight differences can be quite important, such as the difference between a property being read-only versus read/write.

A question in the back: how do we know that the Get-Item cmdlet returns a .NET object? In this case, and in most cases, the easiest way to find out what kind of object we're dealing with (as well as the easiest way to identify the properties and methods available to us) is to bind to the file and then pass that object to the Get-Member cmdlet:

Get-Item c:\scripts\test.txt | Get-Member

The results of this command are shown in Figure 1. As you can see, this really is a .NET object (as if the Scripting Guys would have been wrong about something like that!)

Figure 1 Members of the Object Returned by Get-Item

   
TypeName: System.IO.FileInfo

Name                      MemberType     Definition
----                      ----------     ----------
AppendText                Method         System.IO.StreamWriter AppendText()
CopyTo                    Method         System.IO.FileInfo CopyTo(String destFileName),
                                         System.IO.FileInfo CopyTo(S...
Create                    Method         System.IO.FileStream Create()
CreateObjRef              Method         System.Runtime.Remoting.ObjRef CreateObjRef(
                                         Type requestedType)
CreateText                Method         System.IO.StreamWriter CreateText()

Incidentally, this simple little command illustrates two of the more interesting (and useful) capabilities of Windows PowerShell: pipelining and meta-information. Although we won't go into technical details today, the basic idea behind pipelining is that you can use a cmdlet to grab an object and then hand that object over to another cmdlet. In this command we've used the Get-Item cmdlet to get an object representing the file C:\Scripts\Test.txt, and then handed that object (as an object) through the pipeline to the Get-Member cmdlet. (The | symbol represents the pipeline in a Windows PowerShell command.)

Get-Member then does its thing, which happens to be retrieving the properties and methods for the retrieved object. This is one of the other nifty features of Windows PowerShell: it's very easy to access meta-information, information about information. By using cmdlets such as Get-Member and Get-Command you can interrogate Windows PowerShell all you want, much in the same way you can write a WMI script to retrieve information about the WMI classes, properties, and methods available to you.

Note Get-Member actually goes one step past what you can do with WMI. Although you can use WMI to get information about WMI objects, you can't use WMI to get information about other COM objects (like, say, FileSystemObject). That's not the case with Windows PowerShell. Get-Member works with any object you can bind to or create. Can't remember the methods available when you use the FileSystemObject? Then run the following command:

New-Object -comobject "Scripting.     FileSystemObject" | Get-Member

This is an incredibly useful—and thus far underutilized—capability.

Nice, huh? If we scroll through the list of properties and methods available to us when working with the file C:\Scripts\Test.txt we'll eventually see this line:

LastWriteTime             Property       System.DateTime LastWriteTime {get;set;}

Interesting. Apparently, LastWriteTime is a property that supports both get (read) and set (write). Does that mean we can use Windows PowerShell to change the value of the file's last write time? Hmmm...

Windows PowerShell Glossary

Alias A customizable name that represents an existing cmdlet. Windows PowerShell provides a number of default aliases that map common shell commands to their cmdlet equivalents.

Cmdlet The smallest unit of functionality in Windows PowerShell, analogous to the built-in commands in other shells.

Command A unit of execution in Windows PowerShell. Commands can include multiple piped operations and can span lines by using the ; character.

Frappuccino A blended iced coffee drink sold by Starbucks.

Monad/MSH/Microsoft Command Shell Former code names for Windows PowerShell.

Object Structured data retrieved from the system or a command. Windows PowerShell manipulates data as .NET objects, enabling you to manipulate object properties by name rather than having to parse text-based command output.

Parameter Options set on a cmdlet on the command line. Windows PowerShell uses an initial hyphen (-) character to designate a parameter.

Pipeline Enables sending the output of one command to the input to another command. Also known as a pipe; represented in commands by the | character.

Profile Settings, loaded when Windows PowerShell starts, that enable you to configure how the shell operates.

Script One or more commands saved as and executed from a file. Windows PowerShell uses a .ps1 extension for script files.

Shell A command-line environment in which you can run both commands and cmdlets.

Variable An object or parameter whose value can be set, changed, or retrieved during command excecution.

Let's go back now and talk about the command that sets the last write time of Test.txt to the current date and time. It looks like this:

$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = Date

As you can see, there really isn't much to this command. To begin with, we declare a variable named $a. Then we use the Get-Item cmdlet to connect to the file C:\Scripts\Test.txt. In turn, we store that object in the variable $a. After typing a semicolon (which enables us to carry out multiple commands on a single line), we set the value of the LastWriteTime property to the current date and time. You know, that almost makes up for the lack of cup holders, doesn't it?

Getting a Little Fancier

Of course, you aren't limited to setting the last write time to the current date and time, either. For example, consider this command:

$b = Get-Date "5/22/2006 8:15 PM";
$a = Get-Item c:\scripts\test.txt; $a.LastWriteTime = $b

This time we've strung together three commands. To begin with, we use the Get-Date cmdlet to create a date-time object equal to 5/22/2006, 8:15 PM. Why didn't we have to use Get-Date to create a date-time object in our first script, the script that sets the last write time to the current date and time? That's easy: the Date command automatically returns a date-time object. (To verify this for yourself, type the following command into the Windows PowerShell console: Date | Get-Member.)

After creating a date-time object equal to the desired date and time, we next use our familiar Get-Item command to get an object representing the file Test.txt (an object we store in the variable $a). Finally, we set the LastWriteTime property to the value represented by the variable $b:

$a.LastWriteTime = $b

Believe it or not, you haven't seen the half of it yet. Take a gander at this innocuous-looking command:

$b = Get-Date "5/22/2006 8:15 PM";
Get-ChildItem c:\scripts | ForEach-Object {$_.LastWriteTime = $b}

What's so interesting about this command? Well, let's take a look at what it does. The first part is easy: once again we're creating a date-time object equivalent to 8:15 PM on 5/22/2006. Notice, however, that in part two we don't use the Get-Item cmdlet; instead, we use this command:

Get-ChildItem c:\scripts

What's the point of that? Well, any time you apply the Get-ChildItem cmdlet to a folder you get back a collection of all the files in that folder. We're going to grab that collection, and then hand all the items in the collection over the pipeline (there's that | symbol again) to the ForEach-Object cmdlet.

And what's ForEach-Object going to do with all those files? Why, it's going to change the value of the LastWriteTime property for each and every one, of course:

ForEach-Object {$_.LastWriteTime = $b}

Note Although this month's column isn't meant to be a beginner's guide to Windows PowerShell, we should note that $_ is a special variable that represents the current item in a For Each loop. It's equivalent to objItem in the following VBScript loop:

For Each objItem in colItems
    Wscript.Echo objItem.Name
Next

When our Windows PowerShell command finishes running, all the files in the folder C:\Scripts will have the same last write time. We'd like to see one of those new-fangled horseless carriages do that!

We could run variations of this command all night. For example, maybe you want to change the last write time for only the .txt files in the folder C:\Scripts? OK; all you had to do was ask:

$b = Get-Date "5/22/2006 8:15 PM"; Get-ChildItem c:\scripts\*.txt | ForEach-Object {$_.LastWriteTime = $b}

Whetting Your Appetite

That's very cool. However, we should note that the point of this column is not to "sell" you Windows PowerShell, nor are we encouraging you to throw out all your VBScript scripts and convert them all to Windows PowerShell. If those scripts do what you want and need them to do then you should leave well enough alone; the same goes for batch files, command-line tools, magic spells, and any other tools you use to manage your computers. Remember, if it ain't broke...

The point is not that Windows PowerShell lets you do the things you're already able to do; the point is that, in part because it provides access to the .NET Framework, Windows PowerShell enables you to do things you haven't been able to do before. Modifying the last access time of a file (or a group of files) is one example of this. Whether or not there are other examples depends on what it is you need to do. It's those capabilities that make Windows PowerShell worth looking into.

For many of you this was probably an unusual introduction to Windows PowerShell: after all, instead of starting with a simple "Hello, world" kind of example we launched right into a multi-part Windows PowerShell command, all the while tossing out terms like pipeline and cmdlet as though everyone knew what we meant by all that. That was intentional. After all, we know that many of you have yet to try Windows PowerShell; we were afraid if we showed you how to retrieve a list of services on a computer you'd simply shrug and go back to whatever you were doing.

Note We hope what you were doing wasn't driving. For many of us it's hard enough to drink three beverages, watch a DVD, and talk on a cell phone while driving, without adding "reading TechNet Magazine" to the mix.

Hey, come on: we did say for many of us. Not for all of us.

The Microsoft Scripting Guys work for— well, are employed by—Microsoft. When not playing/coaching/watching baseball (and various other activities) they run the TechNet Script Center. Check it out at www.scriptingguys.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.