|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionMicrosoft have introduced a new command line shell called PowerShell which was previously known as Monad. The main difference between PowerShell and other shells like cmd, bash, ksh etc. is the pipelining and processing of objects as opposed to plain text. Plain text from existing command line applications etc. can still be processed but in general .Net objects are now sent through the pipeline and processed by cmdlets. As an example the following scripts from ksh and PowerShell show some of the differences between working with text pipelines and object pipelines. $ ps –el | awk ‘{ if ( $6 > (1024*10)) { print $3 } }’ | grep –v PID | xargs kill
PS> get-process | where { $_.VM –gt 10M } | stop-process
Powershell cmdlets follow a verb-noun naming convention, e.g. get-process and can be aliased. set-alias ps get-process
set-alias where where-object
The objects flowing through the pipeline are .Net objects which are self describing with metadata that the CLR includes. So for example if you’re not sure what properties and methods are available on the Process object returned by the get-process cmdlet you can issue the following to find out: get-process | get-member
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
Handles AliasProperty Handles = Handlecount
Name AliasProperty Name = ProcessName
NPM AliasProperty NPM = NonpagedSystemMemorySize
PM AliasProperty PM = PagedMemorySize
VM AliasProperty VM = VirtualMemorySize
WS AliasProperty WS = WorkingSet
…...Net class libraries can also be used directly by PowerShell scripts. See the following example which demonstrates the use of the .Net System.Net.WebClient and XML classes to download an RSS feed and print out the title of the feed and the title for each item in the feed. $wc = new-object System.Net.WebClient
$rssdata = [xml]$wc.DownloadString(‘http://blogs.msdn.com/PowerShell/rss.xml’)
write-host $rssdata.rss.channel.title
$rssdata.rss.channel.item | foreach { write-host $_.title }Users or developers can write their own cmdlets to extend the functionality of PowerShell. These cmdlets can either be written using PowerShell script or they can be compiled into a .Net assembly using any programming language that targets the CLR.
For more details take a look at Microsoft’s Scripting Center. Windows Desktop Search cmdletI have previously used the Windows Desktop Search API in order to write a music browser and thought it would be useful to create a Windows Desktop Search cmdlet to allow users to query their search index from the command line. The results of the query can then be pipelined to further cmdlets to act on the results or to simply display the result set. A simple example that a user or an administrator of a system may want to execute is to find all files that are larger than 50MB. You would normally execute something along these lines: dir c:\* -recurse | where { $_.Length -gt 50M }
The equivalent using the Windows Desktop Search cmdlet would be: get-wds “size:>52428800”
The main advantage of the WDS cmdlet in this case is that the query is being run against the search engine’s database index and so it is much quicker to execute compared to the standard method of having to recurse the file system and thrashing the disk while reading in all the associated file system data structures in order to retrieve file’s size. In addition using the WDS cmdlet allows you to issue queries against a large number of attributes that don’t exist with a standard FileInfo object. For a couple of examples take a look at the following WDS cmdlet queries: get-wds “kind:music genre:rock year:1980” | copy-item –destination c:\rock-collection-1980
get-wds “kind:presentations keywords:conference importance:high”
get-wds “kind:pics datetaken:this month cameramake:pentax” | copy-item –destination c:\LatestPentaxSnaps
For more information on the available attributes that you can use for searching and their associated syntax take a look at the Advanced Query Syntax for Windows Desktop Search. ImplementationIn order to implement a PowerShell cmdlet you need to create a class that derives from
The We specify that we require a Query string either as a value from the pipeline or as the 1st argument to our cmdlet. The details on how we require input via either the pipeline or as command line arguments is done declaratively using CLR attributes as show in the example below. There is no need to write command line processing code to validate the input as the PowerShell will handle that on our behalf based on reading the attributes we specify. [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true), ValidateNotNull()]
public string Query
{
get { return query; }
set { query = value; }
}So once our BeginProcessing() method is called the PowerShell would already have set our Query property for us either from the pipeline or from the command line argument. In our BeginProcessing() method we execute the required query using the WDS API. One slight complication is that the WDS API consists of a COM interface that is only supported in a Single Threaded Apartment (STA) where as PowerShell cmdlets aren’t run in STAs. To work around this we create a new thread to perform the actual query and set it to use an STA. We then block on this query thread waiting for it to finish executing. protected override void BeginProcessing()
{
Thread queryThread = new Thread(ExecuteQuery);
queryThread.SetApartmentState(ApartmentState.STA);
queryThread.Start();
queryThread.Join();
}
In the The result is a COM based private void ExecuteQuery()
{
string finalQuery = query + " store:files";
SearchDesktopClass search = new SearchDesktopClass();
_Recordset rs = search.ExecuteQuery(finalQuery, "Url", null, "");
OleDbDataAdapter dbAdapter = new OleDbDataAdapter();
DataSet ds = new DataSet();
DataTable table = new DataTable("MyIndex");
table.Columns.Add("url", typeof(string));
ds.Tables.Add(table);
dbAdapter.Fill(ds, rs, "MyIndex");
foreach (DataRow row in table.Rows)
{
Uri uri = new Uri((string)row[0]);
results.Add(new FileInfo(uri.LocalPath));
}
dbAdapter.Dispose();
}Finally our implementation of the ProcessRecord() method simply iterates over our List<FileInfo> collection that our query thread has populated and writes each FileInfo object in the collection to the pipeline stream. protected override void ProcessRecord()
{
foreach (FileInfo file in results)
{
WriteObject(file);
}
}
|
||||||||||||||||||||||
|
PermaLink |
Privacy |
Terms
of Use
Last Updated: 28 Jun 2006 Editor: |
Copyright 2006 by Sean McLeod Everything else Copyright © CodeProject, 1999-2008 Web12 | Advertise on the Code Project |