Overview
Windows PowerShell is a new command-line shell and scripting language created by Microsoft. Why yet another shell, you ask? Well, PowerShell is different. Of course, every new shell claims to be “different.” However, few, if any, live up to this claim, but PowerShell has some features that truly distinguish it from other shells. In this article, you will look at some of the PowerShell scripting language features and create a PowerShell script from scratch.
A Brief History of the Windows Command-Line Shell
Since the introduction of Windows NT, CMD.EXE has been the command-line shell for Windows. Although CMD.EXE provided an improved command-line experience over its DOS predecessor, COMMAND.COM, it still relied on a relatively primitive scripting language, Windows Command (.CMD and .BAT) files. The later addition of Windows Scripting Host, and the VBScript and JScript languages, greatly improved the scripting capabilities of the shell.
These technologies combined to form a fairly comprehensive command-line shell and scripting environment. There is not really a question of how much you can accomplish with CMD.EXE, .CMD files, and Windows Scripting Host. Rather, the primary complaint is the proverbial “hoops to jump through” to accomplish some seemingly simple task.
Using this “framework” of command-line and scripting tools, any moderately complex script requires a mix of batch commands, Windows Scripting Host, and stand-alone executables. Each of these, in turn, use different conventions for execution and requesting, parsing, and returning data.
Weak variable support in CMD.EXE, inconsistent interfaces, and limited access to Windows settings combined with one other weakness make command-line scripting more difficult than it has to be. What is the one other weakness, you ask? Text. With these technologies, everything is text. The output of one command or script is text and must be parsed and re-formatted to work as input for the next command. This is where PowerShell makes a radical departure from all traditional shells.
PowerShell Scripts == Batch Files on Steroids
PowerShell itself is written in a .NET language and relies heavily on the .NET Framework. Because of this, PowerShell was designed from the ground up as an object-oriented shell and scripting language. Everything in PowerShell is an object and has the full capabilities of the .NET Framework. A command returns a set of objects that can then utilized by using the properties and methods of that type of object. When you want to pipe the output of one command into another command, PowerShell actually passes the objects, not just the textual output of the first command. This gives the next command in the pipeline full access to all of the objects properties and methods.
The treatment of everything as an object, and the ability to pass objects between commands is a major shift of philosophy for command-line shells. That said, PowerShell still functions much like a traditional shell. Commands, scripts, and executables can be typed and ran from the command-line and the results are displayed in text. Windows .CMD and .BAT files, VBScripts, JScripts, and executables that work in CMD.EXE all still run in PowerShell. However, because they are not object-oriented, they do not have full access to the objects created and used in PowerShell. These legacy scripts and executables will still treat everything as text, but you can intermingle PowerShell with these technologies. This is very important if you want to start using PowerShell, but have a complex set of existing scripts that you are not able to convert all at once.
A PowerShell Script
Reading about great new technology is one thing, but seeing it and using it makes comprehension easier. In the rest of this article, you will develop a PowerShell script as a demonstration of PowerShell’s capabilities and ease of use.
DIR is one of the most-used command in CMD.EXE. It lists the files and folders contained in a folder, as shown in Figure 1. Along with the names of each, it shows the last modified date and time, and the size of each file. DIR also shows the combined size of all the files in the specified folder, as well as the total number of files and the total number of sub-folders.
Running DIR in PowerShell also produces a directory listing. However, as Figure 2 shows, it looks a little different. PowerShell does not have a native command called DIR, but it does have one called Get-ChildItem, which performs a similar function. In PowerShell, DIR is an alias for Get-ChildItem; I am not going to cover aliases in this article, but you can think of DIR in PowerShell as a shortcut for Get-ChildItem.
DIR in PowerShell provides much of the same information: a file and folder listing, the last modified date and time, and the size of each file. However, it is missing the summary information that DIR in CMD.EXE provides: the total size of all the files in the folder, the total number of files, and the total number of sub-folders.
For your script, you will create a PowerShell script that will mimic the CMD.EXE DIR command. In the next section, I will explain the key parts of the script.
DIR.PS1: The Header
A PowerShell script consists of PowerShell commands in a plain text file with a .PS1 extension. For your DIR replacement, you will use a text file called DIR.PS1.
To run the script, type the following at a PowerShell prompt:
.\DIR.PS1 X:\Folder
where X is a drive letter and Folder is the name of a folder.
The first part of your script will display the same header information that the CMD.EXE DIR command produces. You need to determine the drive letter of the path that was passed into the script. Arguments to scripts are stored in an array called $args (all PowerShell variables begin with a $). To keep things simple for now, you are only going to deal with the first argument that was passed into your script and ignore the rest. Therefore, you will create a variable called $drive and set it equal to $args[0] (0 is the first element of all arrays in PowerShell).
$drive = $args[0].SubString(0, 1).ToUpper()
To get some of the information you need about the drive, you will have to utilize Windows Management Instrumentation (WMI). The details of WMI is outside of the scope of this article, but the following PowerShell code is pretty easy to understand. You create a variable, $filter, to use with the Get-WmiObject command. The filter you have created tells the command that you only want information about a particular disk. The results of the Get-WmiObject command are stored in a variable called $volInfo. Remember, in PowerShell everything is an object; $volInfo is now an object returned from Get-WmiObject.
$filter = "DeviceID = '" + $drive + ":'" $volInfo = Get-WmiObject -Class Win32_LogicalDisk -Filter $filter
You now have access to all of the properties and methods associated with this object. The serial number of the drive can be accessed via the VolumeSerialNumber property. The serial number is returned as an 8-character string, but you want to format it as the first 4 characters followed by a dash, and then the last 4 characters. In the line below, the backtick character at the end of the first line is the PowerShell line continuation character. Basically, it just tells PowerShell that the line continues on the next line. Splitting the line is not required, but I did it here to decrease the width of the line and to improve the readability of the script.
$serial = $volInfo.VolumeSerialNumber.SubString(0, 4) + "-" + ` $volInfo.VolumeSerialNumber.SubString(4, 4)
Now that you have the $volInfo object, you can write the DIR header information to the screen. If a volume has no label, the text written to the screen is slightly different than if the volume does have a label. A simple If-Else statement is used to determine whether the VolumeName property is equal to an empty string. The Write-Host command is used to write each line to the screen.
If ($volInfo.VolumeName -eq "") { Write-Host (" Volume in drive " + $drive + " has no label") } Else { Write-Host (" Volume in drive " + $drive + " is " + $volInfo.VolumeName) } Write-Host (" Volume Serial Number is " + $serial) Write-Host ("`n Directory of " + $args[0] + "`n")
The “`n” at the beginning and end of the last Write-Host command is used to insert a newline before and after the text. The Write-Host command adds a newline at the end of each line itself, so the effect of the “`n” is to make a blank line before and after that line of text.
Did you notice the “-eq” in the If statement? That is the equality comparison operator. The table below shows all of the comparison operators:
-eq, -ieq | Equals |
-ne, -ine | Not equals |
-gt, -igt | Greater than |
-ge, -ige | Greater than or equal |
-lt, -ilt | Less than |
-le, -ile | Less than or equal |
The -i versions of the comparison operators are the case-insensitive versions.
Figure 3 shows the output of the script you have so far.