Summary: There are times when piling Windows PowerShell cmdlets together becomes a bit cumbersome, so instead of writing a script, consider functions.
Weekend Scripter
Microsoft Scripting Guy, Ed Wilson, is here. There are times, when I want to do things that are a bit more complicated than piecing together cmdlets in a straightforward manner. In yesterday’s Hey! Scripting Guy blog, I talked about not needing to write Windows PowerShell scripts. I illustrated piping cmdlets together, and working in an interactive fashion.
I actually had the idea for this series while I was speaking at the TechStravaganza in Atlanta, Georgia a month or so ago. I told them, that I was making an announcement to ITPros that it was time to quit writing scripts. Here is a picture taken of me while I was speaking on that occasion. In the picture, you can see Mark Schill, president of the Atlanta PowerShell User Group on the left, and Mark Minassi sitting in the front row center.
In the old days, I might very well whip out a script editor and bang out a simple Windows PowerShell script. This was especially true in the Windows PowerShell 1.0 days. For example, consider a common need: to view information about disk drives on a local or remote computer. The information I normally need is things like the capacity of the drive and the amount of free space. In addition, I like to know the percentage of free space on the drive because it gives me a “feel” for how rapidly my drive space is diminishing over time. Anyway, such a script is the DiskDriveScript.ps1 Windows PowerShell script. It is shown here.
DiskDriveScript.ps1
$drive = “c:”
$computername = “.”
Foreach($d in $drive)
{
Get-WmiObject -Class win32_Volume -ComputerName $computername `
-Filter “DriveLetter = ‘$d'” |
Format-table -autosize -property DriveLetter, Label,
@{Name = “ComputerName”; Expression = {$_.__Server} },
@{Name = “Capacity(GB)”; Expression = {$_.capacity / 1GB} },
@{Name = “FreeSpace(GB)”; Expression = {$_.Freespace / 1GB} },
@{Name = “PercentFree”; Expression = { ($_.FreeSpace / $_.Capacity)*100 } }
} # end foreach
The DiskDriveScript.ps1 is not a bad script. It has default values for the disk drive and the computer name. It will accept an array of drive letters, and it creates a nice custom formatted table of output. In addition, it calculates the percent of free space, and it displays the disk space in gigabytes, which is a number that is more likely to be meaningful with today’s drive sizes.
However, there are a number of problems with the DiskDriveScript.ps1 script. The main problem is that it is pretty much single purpose. Although I can call the script and redirect the output to a text file, that is about as far as I can go with the script. One reason is that it outputs data in a table. When the WMI Win32_Volume object hits the Format-Table cmdlet it changes from a management object to a series of format-specific objects. Once a Format-Table, Format-List, or Format-Wide cmdlet enters the equation, it becomes the end of the pipeline—nothing else can follow. In creating the table from within the DiskDriveScript.ps1 script, any potential code reuse is no longer possible.
A more useful approach, and one that takes very little additional work, creates an advanced function instead of a single purpose script. An advanced function does require comment-based Help, but because comment-based Help is so easy to add (especially by using my Add-Help add on to the Windows PowerShell ISE), and because it adds so much value to your function (by explaining it and illustrating potential ways to use the function), I pretty much feel that it is nearly a requirement for an advanced function. This is why I emphasized comment-based Help during the 2011 Scripting Games.
The big change from the DiskDrive.ps1 script was real simple—I changed Format-Table to Select-Object. That is it. That simple change converts the script from a single purpose, limited-use script into a script that emits an object that is available for nearly an infinite amount of code reuse.
For example, suppose I am interested in the amount of free space I have on the various drives that are connected to my computer. I can use the Get-DiskDrive function to retrieve drive information, and then pipe the output to the Sort-Object cmdlet. This command is shown here.
Get-DiskDrive -d c:,e:,g: | Sort-Object -Property percentFree
The command and its associated output are shown in the following image.
Perhaps I am interested in seeing information about how my drives are formatted. I can group the drives, based on the FileSystem property. This command and its associated output are shown here.
PS C:\Users\ed> Get-DiskDrive -d c:,e:,g: | Group-Object -Property FileSystem -NoElement
Count Name
—– —-
2 NTFS
1 FAT32
If I want a bit more information, I remove the NoElement parameter. The command and output associated with the revised command are shown here.
PS C:\Users\ed> Get-DiskDrive -d c:,e:,g: | Group-Object -Property FileSystem
Count Name Group
—– —- —–
2 NTFS {@{DriveLetter=C:; Label=; FileSystem=NTFS; PageFilePrese…
1 FAT32 {@{DriveLetter=G:; Label=TESTFORMAT; FileSystem=FAT32;
Maybe I need to know where the page file resides so I can optimize my system. Well, to do that, I add a Where-Object as shown here.
PS C:\Users\ed> Get-DiskDrive -d c:,e:,g: | where { $_.pagefilepresent }
DriveLetter : C:
Label :
FileSystem : NTFS
PageFilePresent : True
ComputerName : NEWMRED
Capacity(GB) : 119.142574310303
FreeSpace(GB) : 72.1981544494629
PercentFree : 60.5981152139833
But, maybe I need to know the percentage of free space, the file system, and the drive letter. I add a Select-Object command as shown here.
PS C:\Users\ed> Get-DiskDrive -d c:,e:,g: | where { $_.pagefilepresent } | select DriveLetter, Filesystem, percentFree
DriveLetter FileSystem PercentFree
———– ———- ———–
C: NTFS 60.5981152139833
If I want to do so, I can even use WMI to retrieve a list of all the drives on the computer that are fixed local hard disk drives, and pipe that information to the Get-DiskDrive function. This command is shown here (the % character is an alias for the ForEach-Object cmdlet).
gwmi win32_logicaldisk -filter “drivetype = ‘3’” | % {Get-DiskDrive -drive $_.deviceID }
The complete Get-DiskDrive advanced function is shown here.
Function Get-DiskDrive
{
<#
.Synopsis
This function returns capacity and freespace in gigs, and percent free
.Description
This function returns capacity and freespace in gigs, and percent free. By
default it returns the system drive (normally drive c:)
.Example
Get-DiskDrive
Returns capacity and free space in gigabytes. It also returns percent free,
and the drive letter and drive label of the system drive on the local machine.
.Example
Get-DiskDrive -drive e: -computer berlin
Returns capacity and free space in gigabytes of the e: drive. It also returns
percent free, and the drive letter and drive label of the system drive on the
remote machine named berlin.
.Example
Get-DiskDrive -drive e: -computer berlin, munich
Returns capacity and free space in gigabytes of the e: drive. It also returns
percent free, and the drive letter and drive label of the system drive on two
remote machines named berlin and munich.
.Example
Get-DiskDrive -drive c:, e: -computer berlin, munich
Returns capacity and free space in gigabytes of the C: and e: drive. It also
returns percent free, and the drive letter and drive label of the system drive
on two remote machines named berlin and munich.
.Example
“c:”,”d:”,”f:” | % { Get-DiskDrive $_ }
Returns information about c, d, and f drives on local computer.
.Example
Get-DiskDrive -d “c:”,”d:”,”f:”
Returns information about c, d, and f drives on local computer. Same command
as the previous example – but easier to read. But on my computer this is a
bit slower than the previous command (40 milliseconds).
.Parameter drive
The drive letter to query. Defaults to system drive (normally c:)
.Parameter computername
The name of the computer to query. Defaults to local machine.
.Notes
NAME: Example-
AUTHOR: ed wilson, msft
LASTEDIT: 06/02/2011 16:12:08
KEYWORDS:
HSG: HSG-06-26-2011
.Link
Http://www.ScriptingGuys.com/blog
#Requires -Version 2.0
#>
Param(
[string[]]$drive = $env:SystemDrive,
[string[]]$computername = $env:COMPUTERNAME
) #end param
Foreach($d in $drive)
{
Get-WmiObject -Class win32_Volume -ComputerName $computername -Filter “DriveLetter = ‘$d'” |
Select-object DriveLetter, Label, FileSystem, PageFilePresent,
@{Name = “ComputerName”; Expression = {$_.__Server} },
@{Name = “Capacity(GB)”; Expression = {$_.capacity / 1GB} },
@{Name = “FreeSpace(GB)”; Expression = {$_.Freespace / 1GB} },
@{Name = “PercentFree”; Expression = { ($_.FreeSpace / $_.Capacity)*100 } }
} # end foreach
} #end function get-diskdrive
The complete Get-DiskDrive function is uploaded to the Scripting Guys Script Repository. This makes it easy to copy the function without worrying about getting transient HTML goop embedded in the code.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments