More on PowerShell

Microsoft's PowerShell is radically different than shells on UNIX or GNU Linux systems in that Powershell can deal in objects rather than just plain text.

A concrete example may help you more quickly understand the difference.  Suppose you want to get and save information about all the files in a certain subdirectory.   We want to get not only the names of the files but as much metadata as possible relating to each file such as date of creation, date of modification, etc.  This information also needs to be stored in a single XML document.  To keep the size of this post manageable, our subdirectory contains only two files, i.e. file.xml and file.xsl, as shown below.



On a UNIX or GNU Linux system, this would be a somewhat difficult task to quickly accomplish.  Using Powershell, however, it becomes a simple one or two line task.

Because Powershell is fundamentally object-orientated, it regards a filesystem as being an object representated as a location exposed by provider.  More generally, a location can be a file directory, a registry hive, a certificate store or some other "thing" exposed by a provider.  The Powershell get-childItem cmdLet is used to retrieve information, including metadata, about items in a specified location.  Thus get-childItem can be used for a lot of different types of operations including enumerating directory contents, listing registry values or showing the current values of variables.  For our purposes, get-childItem exposes information about files in a specified subdirectory or the current directory if no directory is specified.

Here is a single line Powershell script which does what we want to do.   It retrieves metadata about the two files in the current directory using the get-childItem cmdLet, pipes that data to a cmdLet called ConvertTo-XML which in turn converts the metadata into a valid XML document.  The save method is then used to write this stream out to a file called file.out in the current directory.
(get-childItem | ConvertTo-XML -NoTypeInformation).save("$(convert-path "X:")\file.out")
Alternatively, we could store the retrieved metadata in a variable and then write it out to a file as shown below.  Similar to bash and ksh93, Powershell supports the concept of aliases and defines ls as one of the aliases for the get-childItem cmdLet,dir and gci being the other two.
$a=(ls | ConvertTo-XML)
$a.save("$(convert-path "X:")\file.out")
Here is the contents of file.out.  As you can see, quite a bit of information about each of the two files is exposed.
<?xml version="1.0"?>
<Objects>
<Object Type="System.IO.FileInfo">
<Property Name="PSPath" Type="System.String">Microsoft.PowerShell.Core\FileSystem::C:\PowerShell\file23.xml</Property>
<Property Name="PSParentPath" Type="System.String">Microsoft.PowerShell.Core\FileSystem::C:\PowerShell</Property>
<Property Name="PSIsContainer" Type="System.Boolean">False</Property>
<Property Name="BaseName" Type="System.String">file23</Property>
<Property Name="Mode" Type="System.String">-a---</Property>
<Property Name="Name" Type="System.String">file23.xml</Property>
<Property Name="Length" Type="System.Int64">294</Property>
<Property Name="DirectoryName" Type="System.String">C:\Windows\SUA\home\fpm\PowerShell</Property>
<Property Name="Directory" Type="System.IO.DirectoryInfo">C:\Windows\SUA\home\fpm\PowerShell</Property>
<Property Name="IsReadOnly" Type="System.Boolean">False</Property>
<Property Name="Exists" Type="System.Boolean">True</Property>
<Property Name="FullName" Type="System.String">C:\Windows\SUA\home\fpm\PowerShell\file23.xml</Property>
<Property Name="Extension" Type="System.String">.xml</Property>
<Property Name="CreationTime" Type="System.DateTime">12/17/2008 2:22:17 PM</Property>
<Property Name="CreationTimeUtc" Type="System.DateTime">12/17/2008 6:22:17 AM</Property>
<Property Name="LastAccessTime" Type="System.DateTime">12/17/2008 2:22:17 PM</Property>
<Property Name="LastAccessTimeUtc" Type="System.DateTime">12/17/2008 6:22:17 AM</Property>
<Property Name="LastWriteTime" Type="System.DateTime">12/17/2008 1:25:15 PM</Property>
<Property Name="LastWriteTimeUtc" Type="System.DateTime">12/17/2008 5:25:15 AM</Property>
<Property Name="Attributes" Type="System.IO.FileAttributes">Archive</Property>
</Object>
<Object Type="System.IO.FileInfo">
<Property Name="PSPath" Type="System.String">Microsoft.PowerShell.Core\FileSystem::C:\PowerShell\file23.xsl</Property>
<Property Name="PSParentPath" Type="System.String">Microsoft.PowerShell.Core\FileSystem::C:\PowerShell</Property>
<Property Name="PSChildName" Type="System.String">file23.xsl</Property>
<Property Name="BaseName" Type="System.String">file23</Property>
<Property Name="Mode" Type="System.String">-a---</Property>
<Property Name="Name" Type="System.String">file23.xsl</Property>
<Property Name="Length" Type="System.Int64">459</Property>
<Property Name="DirectoryName" Type="System.String">C:\Windows\SUA\home\fpm\PowerShell</Property>
<Property Name="Directory" Type="System.IO.DirectoryInfo">C:\Windows\SUA\home\fpm\PowerShell</Property>
<Property Name="IsReadOnly" Type="System.Boolean">False</Property>
<Property Name="Exists" Type="System.Boolean">True</Property>
<Property Name="FullName" Type="System.String">C:\Windows\SUA\home\fpm\PowerShell\file23.xsl</Property>
<Property Name="Extension" Type="System.String">.xsl</Property>
<Property Name="CreationTime" Type="System.DateTime">12/17/2008 2:22:17 PM</Property>
<Property Name="CreationTimeUtc" Type="System.DateTime">12/17/2008 6:22:17 AM</Property>
<Property Name="LastAccessTime" Type="System.DateTime">12/17/2008 2:22:17 PM</Property>
<Property Name="LastAccessTimeUtc" Type="System.DateTime">12/17/2008 6:22:17 AM</Property>
<Property Name="LastWriteTime" Type="System.DateTime">12/17/2008 2:40:27 PM</Property>
<Property Name="LastWriteTimeUtc" Type="System.DateTime">12/17/2008 6:40:27 AM</Property>
<Property Name="Attributes" Type="System.IO.FileAttributes">Archive</Property>
</Object>
</objects>
Now suppose requirements changed and we had to save the metadata as a comma seperated value (CSV) file.  Again this is easy to do in Powershell using the Export-CSV cmdLet.
get-childItem | Export-CSV ./file.out
Alternatively, as before, you could store the metadata first in a variable and then writing it out to file in the required format.
$a=(ls)
$a | Export-CSV ./file.out
Here is what file.out now contains.
"Microsoft.PowerShell.Core\FileSystem::C:\Windows\SUA\home\fpm\psh\file.xml","Microsoft.PowerShell.Core\FileSystem::C:\W
indows\SUA\home\fpm\psh","file.xml","X","Microsoft.PowerShell.Core\FileSystem","False", "file","-a---","file.xml","294",
"C:\Windows\SUA\home\fpm\psh","C:\Windows\SUA\home\fpm\psh","False","True","C:\Windows\SUA\home\fpm\psh\file.xml",".xml"
,"12/17/2008 2:22:17 PM","12/17/2008 6:22:17 AM","12/17/2008 2:22:17 PM","12/17/2008 6:22:17 AM","12/17/2008 1:25:15 PM"
,"12/17/2008 5:25:15 AM","Archive"
"Microsoft.PowerShell.Core\FileSystem::C:\Windows\SUA\home\fpm\psh\file.xsl","Microsoft.PowerShell.Core\FileSystem::C:\W
indows\SUA\home\fpm\psh","file.xsl","X","Microsoft.PowerShell.Core\FileSystem","False", "file","-a---","file.xsl","459",
"C:\Windows\SUA\home\fpm\psh","C:\Windows\SUA\home\fpm\psh","False","True","C:\Windows\SUA\home\fpm\psh\file.xsl",".xsl"
,"12/17/2008 2:22:17 PM","12/17/2008 6:22:17 AM","12/17/2008 2:22:17 PM","12/17/2008 6:22:17 AM","12/17/2008 2:40:27 PM"
,"12/17/2008 6:40:27 AM","Archive"
As you can see from these simple examples, it is possible to support objects in a command line shell.  I would like to see one or more of the UNIX/GNU Linux shells evolve to also support objects.  It a different paradign to what existing shell script writers on these platforms are accustomed to but it could enable more robust scripts to be written, simplify many existing scripts, and make scripts easier to understand and maintain.  David Korn has publicly said that this is a direction that he is thinking about for ksh93.  It will be interesting to see what he comes up with.

P.S. The above examples are based on Powershell v2.0 CTP2 and do not work on Powershell v1.0.

0 comments:

Post a Comment